fix(v3): overhaul drag-and-drop for Linux reliability and simplify Windows implementation (#4848)

* fix(v3): overhaul drag-and-drop for Linux reliability and simplify Windows

This commit fixes drag-and-drop reliability on Linux and simplifies the
Windows implementation.

## Linux
- Rewrite GTK drag handlers to properly intercept external file drops
- Fix HTML5 internal drag-and-drop being broken when file drop enabled
- Add hover effects during file drag operations
- Fix multiple app instances interfering with each other

## Windows
- Remove native IDropTarget in favor of JavaScript approach (matches v2)
- File drops now handled via chrome.webview.postMessageWithAdditionalObjects

## All Platforms
- Rename EnableDragAndDrop to EnableFileDrop
- Rename data-wails-drop-target to data-file-drop-target
- Rename wails-drop-target-active to file-drop-target-active
- Add comprehensive drag-and-drop documentation

## Breaking Changes
- EnableDragAndDrop -> EnableFileDrop
- data-wails-dropzone -> data-file-drop-target
- wails-dropzone-hover -> file-drop-target-active
- DropZoneDetails -> DropTargetDetails
- Remove WindowDropZoneFilesDropped event (use WindowFilesDropped)

* feat(macos): optimize drag event performance with debouncing and caching

- Add 50ms debouncing to limit drag events to 20/sec (was 120/sec)
- Implement window implementation caching to avoid repeated lookups
- Maintain existing 5-pixel threshold for immediate response
- Keep zero-allocation path with pre-allocated buffers
- Rename linuxDragActive to nativeDragActive for clarity
- Update IMPLEMENTATION.md with optimization details and Windows guidance

Performance improvements:
- 83% reduction in event frequency
- ~6x reduction in CPU/memory usage during drag operations
- Maintains smooth visual feedback with InvokeSync for timer callbacks

* fix(windows): implement proper file drop support for Windows

- Remove incorrect AllowExternalDrag(false) call that was blocking file drops
- Fix message prefix from 'FilesDropped' to 'file:drop:' to match JS runtime
- Fix coordinate parsing for 'file:drop:x:y' format (indices 2,3 not 1,2)
- Add enableFileDrop flag injection to JS runtime during navigation
- Update JS runtime to check enableFileDrop flag before processing drops
- Always call preventDefault() to stop browser navigation on file drags
- Show 'no drop' cursor when file drops are disabled
- Update example to filter file drags from HTML drop zone handlers
- Add documentation for combining file drop with HTML drag-and-drop

* fix(v3): block file drops on Linux when EnableFileDrop is false

- Add disableDND() to intercept and reject external file drags at GTK level
- Show 'no drop' cursor when files are dragged over window
- Allow internal HTML5 drag-and-drop to work normally
- Initialize _wails.flags object in runtime core to prevent undefined errors
- Inject enableFileDrop flag on Linux and macOS (matching Windows)
- Fix bare _wails reference to use window._wails
- Update docs with info about blocked drops and combining with HTML DnD

* fix(darwin): add missing fmt import in webview_window_darwin.go

* fix(macOS): implement hover effects for file drag-and-drop with optimizations

- Added draggingUpdated: handler to track mouse movement during drag operations
- Implemented macosOnDragEnter/Exit/Over export functions for real-time hover state
- Fixed JS function call from '_wails.handlePlatformFileDrop' to correct 'wails.Window.HandlePlatformFileDrop'
- Added EnableFileDrop flag checks to prevent hover effects when file drops are disabled
- Renamed linuxDragActive to nativeDragActive for cross-platform consistency

Performance optimizations:
- Added 50ms debounce to reduce event frequency from ~120/sec to ~20/sec
- Implemented 5-pixel movement threshold for immediate response
- Added window caching with sync.Map to avoid repeated lookups
- Zero-allocation JavaScript calls with pre-allocated 128-byte buffer
- Reduced memory usage to ~18 bytes per event (6x reduction)

Build improvements:
- Updated runtime Taskfile to include documentation generation
- Added docs:build task to runtime build process
- Fixed build order: events → docs → runtime

Documentation:
- Added IMPLEMENTATION.md with optimization details
- Included guidance for Windows implementation

* chore(v3/examples): remove html-dnd-api example

The drag-n-drop example now demonstrates both external file drops
and internal HTML5 drag-and-drop, making this separate example redundant.

* docs(v3): move drag-and-drop implementation details to runtime-internals

- Add drag-and-drop section to contributing/runtime-internals.mdx
- Remove IMPLEMENTATION.md from example (content now in proper docs)
- Covers platform differences, debugging tips, and key files

* fix(v3): remove html-dnd-api from example build list

* fix(v3): remove duplicate json import in application_darwin.go

* fix(v3): address CodeRabbit review feedback

- Fix docs to use app.Window.NewWithOptions() instead of deprecated API
- Add mutex protection to dragOverJSBuffer to prevent race conditions
- Add mutex protection to dragThrottleState fields for thread safety

* docs: add coderabbit pre-push requirement to AGENTS.md

* fix(v3/test): use correct CSS class name file-drop-target-active

* chore(v3/test): remove dnd-test directory

This was a development test file that shouldn't be in the PR.
The drag-n-drop example serves as the proper test case.

* docs(v3): update Windows file drop comment to reflect implemented fix

Remove stale TODO - enableFileDrop flag is now injected in navigationCompleted

* refactor(v3): make handleDragAndDropMessage unexported

Internal method only called by application event loop, not part of public API.
This commit is contained in:
Lea Anthony 2026-01-04 11:08:29 +11:00 committed by GitHub
commit 53c2275fea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 3317 additions and 2201 deletions

View file

@ -135,6 +135,7 @@ For example: `bd create --help` shows `--parent`, `--deps`, `--assignee`, etc.
- Check `bd ready` before asking "what should I work on?"
- Store AI planning docs in `history/` directory
- Run `bd <cmd> --help` to discover available flags
- **ALWAYS run `coderabbit --plain` before committing** to get code analysis and catch issues early
- Do NOT create markdown TODO lists
- Do NOT use external issue trackers
- Do NOT duplicate tracking systems

View file

@ -171,7 +171,11 @@ export default defineConfig({
collapsed: true,
autogenerate: { directory: "features/browser" },
},
{ label: "Drag & Drop", link: "/features/drag-drop" },
{
label: "Drag & Drop",
collapsed: true,
autogenerate: { directory: "features/drag-and-drop" },
},
{
label: "Keyboard",
collapsed: true,

View file

@ -170,6 +170,80 @@ Follow this checklist and you'll keep the cross-platform contract intact.
---
## 9. Drag-and-Drop
File drag-and-drop uses a **JavaScript-first approach** on all platforms. The native layer intercepts OS drag events, but the actual drop handling and DOM interaction happens in JavaScript.
### Flow
1. User drags files from OS over the Wails window
2. Native layer detects the drag and notifies JavaScript for hover effects
3. User drops files
4. Native layer sends file paths + coordinates to JavaScript
5. JavaScript finds the drop target element (`data-file-drop-target`)
6. JavaScript sends file paths + element details to Go backend
7. Go emits `WindowFilesDropped` event with full context
### Platform Implementations
| Platform | Native Layer | Key Challenge |
|----------|--------------|---------------|
| **Windows** | WebView2's built-in drag support | Coordinates in CSS pixels, no conversion needed |
| **macOS** | NSWindow drag delegates | Convert window-relative to webview-relative coords |
| **Linux** | GTK3 drag signals | Must distinguish file drags from internal HTML5 drags |
### Linux: Distinguishing Drag Types
GTK and WebKit both want to handle drag events. The key is checking the drag target type:
```c
static gboolean is_file_drag(GdkDragContext *context) {
GList *targets = gdk_drag_context_list_targets(context);
for (GList *l = targets; l != NULL; l = l->next) {
GdkAtom atom = GDK_POINTER_TO_ATOM(l->data);
gchar *name = gdk_atom_name(atom);
if (name && g_strcmp0(name, "text/uri-list") == 0) {
g_free(name);
return TRUE; // External file drag
}
g_free(name);
}
return FALSE; // Internal HTML5 drag
}
```
Signal handlers return `FALSE` for internal drags (letting WebKit handle them) and `TRUE` for file drags (handling them ourselves).
### Blocking File Drops
When `EnableFileDrop` is `false`, we still need to prevent the browser from navigating to dropped files. Each platform handles this differently:
- **Windows**: JavaScript calls `preventDefault()` on drag events
- **macOS**: JavaScript calls `preventDefault()` on drag events
- **Linux**: GTK signal handlers intercept and reject file drags at the native level
### Key Files
| File | Purpose |
|------|---------|
| `pkg/application/linux_cgo.go` | GTK drag signal handlers (C code in cgo preamble) |
| `pkg/application/webview_window_darwin.go` | macOS drag delegates |
| `pkg/application/webview_window_windows.go` | WebView2 message handling |
| `internal/runtime/desktop/@wailsio/runtime/src/window.ts` | JavaScript drop handling |
### Debugging
- **Linux**: Add `printf` in C code (remember `fflush(stdout)`)
- **Windows**: Use `globalApplication.debug()`
- **JavaScript**: Check browser console, enable debug mode
Common issues:
1. **Internal HTML5 drag not working**: Native handler intercepting it (return `FALSE` for non-file drags)
2. **Hover effects not showing**: JavaScript handlers not being called
3. **Wrong coordinates**: Check coordinate space conversions
---
You now have a guided tour of the runtime internals. Combine this knowledge with
the **Codebase Layout** map and the **Asset Server** docs to navigate confidently
and make impactful contributions. Happy coding!

View file

@ -0,0 +1,212 @@
---
title: File Drop
description: Accept files dragged from the operating system into your application
sidebar:
order: 1
---
Wails lets users drag files from the operating system (file manager, desktop) into your application. Unlike HTML5 drag-and-drop which only works within the browser, this gives you access to actual file paths on disk.
## Enable File Drop
File drop is disabled by default. To enable it, set `EnableFileDrop: true` in your window options:
```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "My App",
Width: 800,
Height: 600,
EnableFileDrop: true,
})
```
When `EnableFileDrop` is `false` (the default), files dragged from the OS are blocked - they won't open in the webview or trigger any events. This prevents accidental navigation when users drag files over your app.
## Define Drop Zones
Drop zones tell Wails which elements should accept files. Files dropped outside a drop zone are ignored.
Add the `data-file-drop-target` attribute to any element:
```html
<div id="upload" class="drop-zone" data-file-drop-target>
Drop files here
</div>
```
You can have multiple drop zones. The element's `id` and CSS classes are passed to your Go code, so you can handle drops differently depending on where files land.
## Style Drag Hover
When files are dragged over a drop zone, Wails adds the `file-drop-target-active` class. This lets you provide visual feedback so users know where they can drop:
```css
.drop-zone {
border: 2px dashed #ccc;
padding: 40px;
text-align: center;
transition: all 0.2s ease;
}
.drop-zone.file-drop-target-active {
border-color: #007bff;
background-color: rgba(0, 123, 255, 0.1);
}
```
The class is removed automatically when files leave the zone or are dropped.
## Detect Dropped Files
When files are dropped on a valid drop zone, Wails fires a `WindowFilesDropped` event. The event context contains the full filesystem paths of all dropped files:
```go
import "github.com/wailsapp/wails/v3/pkg/events"
window.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
files := event.Context().DroppedFiles()
for _, file := range files {
fmt.Println("Dropped:", file)
}
})
```
The paths are absolute, like `/home/user/documents/report.pdf` or `C:\Users\Name\Documents\report.pdf`.
## Get Drop Target Info
When you have multiple drop zones, you can find out which one received the files using `DropTargetDetails()`:
```go
window.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
files := event.Context().DroppedFiles()
details := event.Context().DropTargetDetails()
fmt.Printf("Dropped on element: id=%s, classes=%v\n",
details.ElementID, details.ClassList)
fmt.Printf("Position: x=%d, y=%d\n", details.X, details.Y)
})
```
This lets you route files to different handlers:
```go
switch details.ElementID {
case "images":
handleImageUpload(files)
case "documents":
handleDocumentUpload(files)
}
```
## Complete Example
**Go:**
```go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "File Uploader",
EnableFileDrop: true,
})
window.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
files := event.Context().DroppedFiles()
details := event.Context().DropTargetDetails()
// Send to frontend
app.EmitEvent("files-dropped", map[string]any{
"files": files,
"target": details.ElementID,
})
})
```
**HTML:**
```html
<div id="images" class="drop-zone" data-file-drop-target>
Drop images here
</div>
<div id="documents" class="drop-zone" data-file-drop-target>
Drop documents here
</div>
<style>
.drop-zone {
border: 2px dashed #ccc;
border-radius: 8px;
padding: 40px;
text-align: center;
margin: 20px;
transition: all 0.2s ease;
}
.drop-zone.file-drop-target-active {
border-color: #007bff;
background-color: rgba(0, 123, 255, 0.1);
}
</style>
```
## Full Window Drop
If you want files to be droppable anywhere in your app, add the attribute to the body element:
```html
<body data-file-drop-target>
<!-- Your app content -->
</body>
```
You can use a CSS overlay to indicate the entire window is a drop target:
```css
body.file-drop-target-active::after {
content: "Drop files anywhere";
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: #007bff;
background: rgba(255, 255, 255, 0.9);
pointer-events: none;
}
```
## Combining with HTML Drag & Drop
You can use both external file drops and internal HTML drag-and-drop in the same application. When `EnableFileDrop` is `true`, Wails intercepts external file drags but lets internal HTML5 drags pass through normally.
To distinguish between them in your HTML drop zone handlers, check if the drag contains files:
```javascript
zone.addEventListener('dragenter', (e) => {
// Skip external file drags - Wails handles these
if (e.dataTransfer?.types.includes('Files')) {
return;
}
// Handle internal HTML5 drags
zone.classList.add('drag-over');
});
zone.addEventListener('drop', (e) => {
// Skip external file drops - Wails handles these
if (e.dataTransfer?.types.includes('Files')) {
return;
}
e.preventDefault();
zone.classList.remove('drag-over');
// Handle internal drop
});
```
This ensures your HTML drop handlers only respond to internal drags (like moving list items), while Wails handles external file drops separately via the `WindowFilesDropped` event.
## Next Steps
- [HTML Drag & Drop](./html) - Drag elements within your app
- [Window Options](/features/windows/options) - All window configuration options

View file

@ -0,0 +1,232 @@
---
title: HTML Drag & Drop
description: Drag and drop elements within your application
sidebar:
order: 2
---
HTML5 drag-and-drop lets users drag elements within your app's UI - for example, reordering a list or moving items between columns. This is standard web functionality that works in Wails without any special setup.
## Make an Element Draggable
By default, most elements can't be dragged. To make an element draggable, add `draggable="true"`:
```html
<div class="item" draggable="true">Drag me</div>
```
The element will now show a drag preview when the user clicks and drags it.
## Define a Drop Zone
Elements don't accept drops by default. To make an element accept drops, you need to cancel the default behaviour on `dragover`:
```html
<div class="drop-zone" id="target">Drop here</div>
<script>
const target = document.getElementById('target');
target.addEventListener('dragover', (e) => {
e.preventDefault(); // Allow the drop
});
target.addEventListener('drop', (e) => {
e.preventDefault();
// Handle the drop
});
</script>
```
Calling `preventDefault()` on `dragover` is required - it signals that this element accepts drops. Without it, the drop event won't fire.
## Style Drag Hover
To show users where they can drop, add visual feedback when dragging over a drop zone. The `dragenter` event fires when something enters the zone, and `dragleave` fires when it leaves:
```css
.drop-zone {
border: 2px dashed #ccc;
padding: 40px;
transition: all 0.2s ease;
}
.drop-zone.drag-over {
border-color: #007bff;
background-color: rgba(0, 123, 255, 0.1);
}
```
```javascript
const target = document.getElementById('target');
target.addEventListener('dragenter', () => {
target.classList.add('drag-over');
});
target.addEventListener('dragleave', () => {
target.classList.remove('drag-over');
});
target.addEventListener('drop', (e) => {
e.preventDefault();
target.classList.remove('drag-over');
// Handle the drop
});
```
Note: `dragleave` also fires when entering a child element, which can cause flickering. The complete example below shows how to handle this.
## Complete Example
A task list where items can be dragged between priority columns. This tracks the dragged element in a variable, which is the simplest approach when everything is on the same page:
```html
<div class="tasks">
<div class="item" draggable="true">Fix login bug</div>
<div class="item" draggable="true">Update docs</div>
<div class="item" draggable="true">Add dark mode</div>
</div>
<div class="columns">
<div class="drop-zone" data-priority="high">
<h3>High Priority</h3>
<ul></ul>
</div>
<div class="drop-zone" data-priority="low">
<h3>Low Priority</h3>
<ul></ul>
</div>
</div>
<script>
let draggedItem = null;
// Track which item is being dragged
document.querySelectorAll('.item').forEach(item => {
item.addEventListener('dragstart', () => {
draggedItem = item;
item.classList.add('dragging');
});
item.addEventListener('dragend', () => {
item.classList.remove('dragging');
});
});
// Handle drops on each zone
document.querySelectorAll('.drop-zone').forEach(zone => {
zone.addEventListener('dragover', (e) => {
e.preventDefault();
});
zone.addEventListener('dragenter', () => {
zone.classList.add('drag-over');
});
zone.addEventListener('dragleave', (e) => {
// Only remove the class if we're leaving the zone entirely,
// not just entering a child element
if (!zone.contains(e.relatedTarget)) {
zone.classList.remove('drag-over');
}
});
zone.addEventListener('drop', (e) => {
e.preventDefault();
zone.classList.remove('drag-over');
if (draggedItem) {
const li = document.createElement('li');
li.textContent = draggedItem.textContent;
zone.querySelector('ul').appendChild(li);
draggedItem.remove();
}
});
});
</script>
<style>
.item {
padding: 12px 16px;
background: #f0f0f0;
margin: 8px 0;
border-radius: 8px;
cursor: grab;
}
.item.dragging {
opacity: 0.5;
}
.drop-zone {
min-height: 150px;
border: 2px dashed #ccc;
border-radius: 8px;
padding: 15px;
transition: all 0.2s ease;
}
.drop-zone.drag-over {
border-color: #007bff;
background: rgba(0, 123, 255, 0.1);
}
</style>
```
## Combining with File Drop
If your app uses both HTML drag-and-drop and [File Drop](./files), your HTML drop zones will also receive events when users drag files from the operating system. To prevent confusion, filter out file drags in your handlers:
```javascript
zone.addEventListener('dragenter', (e) => {
// Ignore external file drags
if (e.dataTransfer?.types.includes('Files')) return;
zone.classList.add('drag-over');
});
zone.addEventListener('dragover', (e) => {
// Ignore external file drags
if (e.dataTransfer?.types.includes('Files')) return;
e.preventDefault();
});
zone.addEventListener('drop', (e) => {
// Ignore external file drags
if (e.dataTransfer?.types.includes('Files')) return;
e.preventDefault();
zone.classList.remove('drag-over');
// Handle the internal drop
});
```
The `dataTransfer.types` array contains `'Files'` when the user is dragging files from the OS, but contains types like `'text/plain'` for internal HTML drags. This lets you distinguish between the two.
## Passing Data with dataTransfer
The example above tracks the dragged element in a JavaScript variable. This works well when everything is on the same page. But if you need to drag between iframes or pass data that isn't tied to a DOM element, use the `dataTransfer` API:
```javascript
// When drag starts, store data
item.addEventListener('dragstart', (e) => {
e.dataTransfer.setData('text/plain', item.id);
});
// When dropped, retrieve the data
target.addEventListener('drop', (e) => {
e.preventDefault();
const itemId = e.dataTransfer.getData('text/plain');
const item = document.getElementById(itemId);
// Move or copy the item
});
```
The data is stored as strings, so you'll need to serialize objects with `JSON.stringify()` if needed.
## Next Steps
- [File Drop](./files) - Accept files from the operating system
- [MDN Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) - Full browser API reference

View file

@ -546,6 +546,61 @@ Assets: application.AssetOptions{
**See [Build System](/concepts/build-system) for details.**
## Input Options
### EnableFileDrop
**Type:** `bool`
**Default:** `false`
**Platform:** All
```go
EnableFileDrop: true,
```
**Purpose:** Enable drag-and-drop of files from the operating system into the window.
When enabled:
- Files dragged from file managers can be dropped into your application
- The `WindowFilesDropped` event fires with the dropped file paths
- Elements with `data-file-drop-target` attribute provide detailed drop information
**Use cases:**
- File upload interfaces
- Document editors
- Media importers
- Any app that accepts files
**Example:**
```go
window := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
Title: "File Uploader",
EnableFileDrop: true,
})
// Handle dropped files
window.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
files := event.Context().DroppedFiles()
details := event.Context().DropTargetDetails()
for _, file := range files {
fmt.Println("Dropped:", file)
}
})
```
**HTML drop zones:**
```html
<!-- Mark elements as drop targets -->
<div id="upload" data-file-drop-target>
Drop files here
</div>
```
**See [File Drop](/features/drag-and-drop/files) for complete documentation.**
## Security Options
### ContentProtectionEnabled

View file

@ -24,7 +24,7 @@ Wails provides native desktop capabilities:
- [Events](/features/events/system) - Communication between components
- [Bindings](/features/bindings/methods) - Type-safe Go ↔ JavaScript calls
- [Clipboard](/features/clipboard) - Copy/paste operations
- [Drag & Drop](/features/drag-drop) - File drag and drop
- [Drag & Drop](/features/drag-and-drop/files) - File drag and drop
- [Keyboard](/features/keyboard) - Global shortcuts
## Get Help

View file

@ -181,7 +181,6 @@ tasks:
gin-routing
gin-service
hide-window
html-dnd-api
ignore-mouse
keybindings
liquid-glass
@ -306,7 +305,6 @@ tasks:
gin-routing
gin-service
hide-window
html-dnd-api
ignore-mouse
keybindings
liquid-glass

View file

@ -20,15 +20,28 @@ After processing, the content will be moved to the main changelog and this file
## Changed
<!-- Changes in existing functionality -->
- **BREAKING:** Rename `EnableDragAndDrop` to `EnableFileDrop` in window options
- **BREAKING:** Rename `DropZoneDetails` to `DropTargetDetails` in event context
- **BREAKING:** Rename `DropZoneDetails()` method to `DropTargetDetails()` on `WindowEventContext`
- **BREAKING:** Remove `WindowDropZoneFilesDropped` event, use `WindowFilesDropped` instead
- **BREAKING:** Change HTML attribute from `data-wails-dropzone` to `data-file-drop-target`
- **BREAKING:** Change CSS hover class from `wails-dropzone-hover` to `file-drop-target-active`
- **BREAKING:** Remove `DragEffect`, `OnEnterEffect`, `OnOverEffect` options from Windows (were part of removed IDropTarget)
## Fixed
<!-- Bug fixes -->
- Fix file drag-and-drop on Windows not working at non-100% display scaling
- Fix HTML5 internal drag-and-drop being broken when file drop was enabled on Windows
- Fix file drop coordinates being in wrong pixel space on Windows (physical vs CSS pixels)
- Fix file drag-and-drop on Linux not working reliably with hover effects
- Fix HTML5 internal drag-and-drop being broken when file drop was enabled on Linux
## Deprecated
<!-- Soon-to-be removed features -->
## Removed
<!-- Features removed in this release -->
- Remove native `IDropTarget` implementation on Windows in favor of JavaScript-based approach (matches v2 behavior)
## Security
<!-- Security-related changes -->

View file

@ -1,27 +1,73 @@
# Drag-n-drop Example
# File Drop Example
This example demonstrates how to handle files being dragged into the application.
This example demonstrates how to handle files being dragged from the operating system (Finder, Explorer, file managers) into a Wails application.
Dropped files are automatically categorised by type and displayed in separate buckets: documents, images, or other files.
## How it works
1. Enable file drops in window options:
```go
EnableFileDrop: true
```
2. Mark elements as drop targets in HTML:
```html
<div data-file-drop-target>Drop files here</div>
```
3. Listen for the `WindowFilesDropped` event:
```go
win.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
files := event.Context().DroppedFiles()
details := event.Context().DropTargetDetails()
// Handle the dropped files
})
```
4. Optionally forward to frontend:
```go
application.Get().Event.Emit("files-dropped", map[string]any{
"files": files,
"details": details,
})
```
## Drop Target Details
When files are dropped, you can get information about the drop location:
- `ElementID` - The ID of the element that received the drop
- `ClassList` - CSS classes on the drop target
- `X`, `Y` - Coordinates of the drop within the element
## Styling
When files are dragged over a valid drop target, Wails adds the `file-drop-target-active` class:
```css
.file-drop-target-active {
border-color: #4a9eff;
background: rgba(74, 158, 255, 0.1);
}
```
## Running the example
To run the example, simply run the following command:
```bash
go run main.go
```
## Building the example
Then drag files from your desktop or file manager into the drop zone.
To build the example in debug mode, simply run the following command:
## See also
```bash
wails3 task build
```
- [html-dnd-api](../html-dnd-api) - For dragging elements *within* your application (HTML5 Drag and Drop API)
# Status
## Status
| Platform | Status |
|----------|-------------|
| Mac | Working |
| Windows | Not Working |
| Linux | |
| Platform | Status |
|----------|---------|
| Mac | Working |
| Windows | Working |
| Linux | Working |

View file

@ -3,422 +3,433 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Tree Drag-and-Drop Example</title>
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<!-- Material UI lite CSS -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
<link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
<script defer src="https://code.getmdl.io/1.3.0/material.min.js" integrity="sha384-7/3UJ+C4EZRMEh+yDUhEZJ5YH9Ul3XW1U6AlTjHlyMowgIkG7svPJf0BN3b2CEm1" crossorigin="anonymous"></script>
<title>Drag and Drop Demo</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
background-color: #1e1e1e;
color: #f0f0f0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 20px;
height: 100vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
background: #1a1a2e;
color: #eee;
min-height: 100vh;
}
.container {
display: flex;
height: calc(100vh - 140px);
gap: 20px;
h1 {
margin-top: 40px;
text-align: center;
color: #fff;
}
.file-tree {
flex: 1;
background-color: #2a2a2a;
border-radius: 4px;
padding: 16px;
overflow: auto;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.info-panel {
flex: 1;
background-color: #2a2a2a;
border-radius: 4px;
padding: 16px;
overflow: auto;
display: flex;
flex-direction: column;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.tree-node {
padding: 4px;
margin: 2px 0;
display: flex;
align-items: center;
border-radius: 4px;
cursor: pointer;
}
.tree-node:hover {
background-color: #3a3a3a;
}
.folder {
color: #ffca28; /* Amber for folders */
}
.file {
color: #81c784; /* Light green for files */
}
.node-icon {
margin-right: 8px;
}
.tree-children {
padding-left: 24px;
}
.dropzone {
transition: background-color 0.3s, box-shadow 0.3s;
}
/* Wails applied class for active dropzones during drag */
.wails-dropzone-hover {
background-color: rgba(63, 81, 181, 0.2) !important;
box-shadow: 0 0 8px rgba(63, 81, 181, 0.5) !important;
}
/* Custom styles for folder highlight */
.folder-dropzone {
transition: all 0.2s ease;
border: 1px solid transparent;
}
.folder-dropzone.wails-dropzone-hover {
border: 1px dashed #7986cb;
box-shadow: inset 0 0 5px rgba(121, 134, 203, 0.5) !important;
}
#drop-output {
background-color: #3a3a3a;
color: #f0f0f0;
font-family: monospace;
padding: 16px;
border-radius: 4px;
flex-grow: 1;
white-space: pre-wrap;
overflow: auto;
margin-top: 16px;
}
.header {
margin-bottom: 20px;
h2 {
color: #888;
font-size: 16px;
text-transform: uppercase;
letter-spacing: 1px;
margin: 40px 0 20px;
text-align: center;
}
.info-header {
margin-bottom: 16px;
color: #7986cb;
.instructions {
max-width: 900px;
margin: 0 auto 20px;
text-align: center;
color: #888;
line-height: 1.6;
}
.material-icons {
font-size: 20px;
}
/* Path breadcrumb */
.path-display {
background-color: #3a3a3a;
padding: 8px 16px;
.instructions code {
background: #16213e;
padding: 2px 6px;
border-radius: 4px;
margin-bottom: 16px;
color: #4a9eff;
}
/* ===== External File Drop Section ===== */
.external-section {
max-width: 900px;
margin: 0 auto 40px;
}
.drop-zone {
padding: 40px;
border: 3px dashed #444;
border-radius: 16px;
text-align: center;
transition: all 0.2s ease;
}
.drop-zone p {
margin: 0;
color: #666;
font-size: 18px;
}
/* Wails adds this class when dragging files over */
.file-drop-target-active {
border-color: #4a9eff !important;
background: rgba(74, 158, 255, 0.1) !important;
box-shadow: 0 0 30px rgba(74, 158, 255, 0.2);
}
.file-drop-target-active p {
color: #4a9eff;
}
.buckets {
display: flex;
gap: 20px;
margin-top: 20px;
}
.bucket {
flex: 1;
min-height: 150px;
background: #16213e;
border-radius: 12px;
padding: 15px;
}
.bucket h3 {
margin: 0 0 15px 0;
padding-bottom: 10px;
border-bottom: 1px solid #2a3a5e;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 1px;
}
.bucket.documents h3 { color: #4a9eff; }
.bucket.images h3 { color: #9b59b6; }
.bucket.other h3 { color: #27ae60; }
.bucket ul {
margin: 0;
padding: 0;
list-style: none;
}
.bucket li {
padding: 6px 0;
font-family: monospace;
color: #bbdefb;
overflow-x: auto;
white-space: nowrap;
font-size: 12px;
color: #aaa;
word-break: break-all;
}
.bucket .empty {
color: #444;
font-style: italic;
font-family: inherit;
}
/* ===== Internal Drag Section ===== */
.internal-section {
max-width: 900px;
margin: 0 auto 40px;
}
.internal-container {
display: flex;
gap: 20px;
align-items: flex-start;
}
.draggable-items {
flex: 1;
background: #16213e;
border-radius: 12px;
padding: 15px;
min-height: 200px;
}
.draggable-items h3 {
margin: 0 0 15px 0;
padding-bottom: 10px;
border-bottom: 1px solid #2a3a5e;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 1px;
color: #e67e22;
}
.draggable-item {
background: #2a3a5e;
padding: 12px 16px;
margin-bottom: 8px;
border-radius: 8px;
cursor: grab;
transition: all 0.2s ease;
user-select: none;
}
.draggable-item:hover {
background: #3a4a6e;
}
.draggable-item.dragging {
opacity: 0.5;
cursor: grabbing;
}
.drop-targets {
flex: 2;
display: flex;
gap: 15px;
}
.internal-drop-zone {
flex: 1;
min-height: 200px;
background: #16213e;
border: 2px dashed #333;
border-radius: 12px;
padding: 15px;
transition: all 0.2s ease;
}
.internal-drop-zone h3 {
margin: 0 0 15px 0;
padding-bottom: 10px;
border-bottom: 1px solid #2a3a5e;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 1px;
}
.internal-drop-zone.priority-high h3 { color: #e74c3c; }
.internal-drop-zone.priority-medium h3 { color: #f39c12; }
.internal-drop-zone.priority-low h3 { color: #27ae60; }
.internal-drop-zone.drag-over {
border-color: #4a9eff;
background: rgba(74, 158, 255, 0.1);
}
.internal-drop-zone ul {
margin: 0;
padding: 0;
list-style: none;
}
.internal-drop-zone li {
background: #2a3a5e;
padding: 10px 14px;
margin-bottom: 6px;
border-radius: 6px;
font-size: 13px;
}
.internal-drop-zone .empty {
color: #444;
font-style: italic;
background: none;
padding: 0;
}
/* ===== Info Section ===== */
.drop-info {
max-width: 900px;
margin: 20px auto 0;
padding: 15px;
background: #16213e;
border-radius: 8px;
font-family: monospace;
font-size: 12px;
color: #666;
}
.drop-info strong {
color: #888;
}
</style>
</head>
<body>
<div class="header">
<h1>File Tree Drag & Drop Example</h1>
<p>Drag files onto folders to upload them to that location</p>
<h1>Drag and Drop Demo</h1>
<!-- ===== External File Drop ===== -->
<h2>External File Drop</h2>
<div class="instructions">
<p>
Drop files from your operating system (Finder, Explorer, file managers).
Uses <code>EnableFileDrop: true</code> and <code>data-file-drop-target</code>
</p>
</div>
<div class="container">
<div class="file-tree">
<div class="path-display" id="current-path">/home/user</div>
<!-- Root folder -->
<div class="tree-node folder folder-dropzone" data-path="/home/user" data-wails-dropzone data-folder-id="root" data-folder-name="Home">
<span class="material-icons node-icon">folder</span>
<span>Home</span>
<div class="external-section">
<div class="drop-zone" data-file-drop-target>
<p>Drop files from your desktop or file manager here</p>
</div>
<div class="buckets">
<div class="bucket documents">
<h3>Documents</h3>
<ul id="documents-list">
<li class="empty">No documents yet</li>
</ul>
</div>
<div class="tree-children">
<!-- Documents folder -->
<div class="tree-node folder folder-dropzone" data-path="/home/user/Documents" data-wails-dropzone data-folder-id="docs" data-folder-name="Documents">
<span class="material-icons node-icon">folder</span>
<span>Documents</span>
<div class="bucket images">
<h3>Images</h3>
<ul id="images-list">
<li class="empty">No images yet</li>
</ul>
</div>
<div class="bucket other">
<h3>Other Files</h3>
<ul id="other-list">
<li class="empty">No other files yet</li>
</ul>
</div>
</div>
</div>
<!-- ===== Internal Drag and Drop ===== -->
<h2>Internal Drag and Drop</h2>
<div class="instructions">
<p>
Drag items between zones using the HTML5 Drag and Drop API.
Uses <code>draggable="true"</code> and standard DOM events.
</p>
</div>
<div class="internal-section">
<div class="internal-container">
<div class="draggable-items">
<h3>Tasks</h3>
<div class="draggable-item" draggable="true" data-task="1">Fix login bug</div>
<div class="draggable-item" draggable="true" data-task="2">Update documentation</div>
<div class="draggable-item" draggable="true" data-task="3">Add dark mode</div>
<div class="draggable-item" draggable="true" data-task="4">Refactor API calls</div>
<div class="draggable-item" draggable="true" data-task="5">Write unit tests</div>
</div>
<div class="drop-targets">
<div class="internal-drop-zone priority-high" data-priority="high">
<h3>High Priority</h3>
<ul></ul>
</div>
<div class="tree-children">
<div class="tree-node file" data-path="/home/user/Documents/report.pdf">
<span class="material-icons node-icon">description</span>
<span>report.pdf</span>
</div>
<div class="tree-node file" data-path="/home/user/Documents/notes.txt">
<span class="material-icons node-icon">description</span>
<span>notes.txt</span>
</div>
<div class="internal-drop-zone priority-medium" data-priority="medium">
<h3>Medium Priority</h3>
<ul></ul>
</div>
<!-- Pictures folder -->
<div class="tree-node folder folder-dropzone" data-path="/home/user/Pictures" data-wails-dropzone data-folder-id="pics" data-folder-name="Pictures">
<span class="material-icons node-icon">folder</span>
<span>Pictures</span>
</div>
<div class="tree-children">
<div class="tree-node file" data-path="/home/user/Pictures/vacation.jpg">
<span class="material-icons node-icon">image</span>
<span>vacation.jpg</span>
</div>
<div class="tree-node file" data-path="/home/user/Pictures/profile.png">
<span class="material-icons node-icon">image</span>
<span>profile.png</span>
</div>
</div>
<!-- Downloads folder -->
<div class="tree-node folder folder-dropzone" data-path="/home/user/Downloads" data-wails-dropzone data-folder-id="downloads" data-folder-name="Downloads">
<span class="material-icons node-icon">folder</span>
<span>Downloads</span>
</div>
<div class="tree-children">
<div class="tree-node file" data-path="/home/user/Downloads/app.dmg">
<span class="material-icons node-icon">archive</span>
<span>app.dmg</span>
</div>
<div class="tree-node file" data-path="/home/user/Downloads/data.zip">
<span class="material-icons node-icon">archive</span>
<span>data.zip</span>
</div>
</div>
<!-- Projects folder with nested structure -->
<div class="tree-node folder folder-dropzone" data-path="/home/user/Projects" data-wails-dropzone data-folder-id="projects" data-folder-name="Projects">
<span class="material-icons node-icon">folder</span>
<span>Projects</span>
</div>
<div class="tree-children">
<!-- Nested folder with its own dropzone -->
<div class="tree-node folder folder-dropzone" data-path="/home/user/Projects/Wails" data-wails-dropzone data-folder-id="wails" data-folder-name="Wails">
<span class="material-icons node-icon">folder</span>
<span>Wails</span>
</div>
<div class="tree-children">
<div class="tree-node file" data-path="/home/user/Projects/Wails/main.go">
<span class="material-icons node-icon">code</span>
<span>main.go</span>
</div>
<div class="tree-node file" data-path="/home/user/Projects/Wails/go.mod">
<span class="material-icons node-icon">code</span>
<span>go.mod</span>
</div>
</div>
<div class="internal-drop-zone priority-low" data-priority="low">
<h3>Low Priority</h3>
<ul></ul>
</div>
</div>
</div>
<div class="info-panel">
<h2 class="info-header">Drop Information</h2>
<div id="drop-output">Drag and drop files onto any folder in the file tree...
</div>
<div class="drop-info" id="drop-info">
<strong>Last action:</strong> <span id="drop-details">No actions yet</span>
</div>
The folder elements have the following attributes:
- data-wails-dropzone: Marks the element as a dropzone
- data-path: Contains the full path (used for destination)
- data-folder-id: A unique ID for the folder
- data-folder-name: The display name of the folder</div>
</div>
</div>
<script type="module">
import * as wails from "/wails/runtime.js";
import { Events } from '/wails/runtime.js';
const outputDiv = document.getElementById('drop-output');
const pathDisplay = document.getElementById('current-path');
const folderNodes = document.querySelectorAll('.folder-dropzone');
const documentsEl = document.getElementById('documents-list');
const imagesEl = document.getElementById('images-list');
const otherEl = document.getElementById('other-list');
const dropDetails = document.getElementById('drop-details');
// Add debug coordinate overlay
let debugOverlay = null;
// ===== External File Drop =====
const imageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.svg', '.webp', '.ico', '.tiff', '.tif'];
const documentExtensions = ['.pdf', '.doc', '.docx', '.txt', '.rtf', '.odt', '.xls', '.xlsx', '.ppt', '.pptx', '.md', '.csv', '.json', '.xml', '.html', '.htm'];
function createDebugOverlay() {
if (debugOverlay) return;
debugOverlay = document.createElement('div');
debugOverlay.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
border-radius: 5px;
font-family: monospace;
font-size: 12px;
z-index: 10000;
pointer-events: none;
white-space: pre-line;
`;
document.body.appendChild(debugOverlay);
function getFileName(path) {
return path.split(/[/\\]/).pop();
}
function updateDebugOverlay(info) {
if (!debugOverlay) createDebugOverlay();
debugOverlay.textContent = info;
function getExtension(path) {
const name = getFileName(path);
const idx = name.lastIndexOf('.');
return idx > 0 ? name.substring(idx).toLowerCase() : '';
}
// Track mouse position and other debug info
let mouseInfo = { x: 0, y: 0 };
let windowInfo = { width: window.innerWidth, height: window.innerHeight };
function categoriseFile(path) {
const ext = getExtension(path);
if (imageExtensions.includes(ext)) return 'images';
if (documentExtensions.includes(ext)) return 'documents';
return 'other';
}
document.addEventListener('mousemove', (e) => {
mouseInfo.x = e.clientX;
mouseInfo.y = e.clientY;
function addFileToList(listEl, fileName) {
const empty = listEl.querySelector('.empty');
if (empty) empty.remove();
const debugInfo = `Mouse: ${e.clientX}, ${e.clientY}
Page: ${e.pageX}, ${e.pageY}
Screen: ${e.screenX}, ${e.screenY}
Window: ${windowInfo.width}x${windowInfo.height}
Viewport offset: ${window.pageXOffset}, ${window.pageYOffset}`;
const li = document.createElement('li');
li.textContent = fileName;
listEl.appendChild(li);
}
Events.On('files-dropped', (event) => {
const { files, details } = event.data;
updateDebugOverlay(debugInfo);
files.forEach(filePath => {
const fileName = getFileName(filePath);
const category = categoriseFile(filePath);
switch (category) {
case 'documents':
addFileToList(documentsEl, fileName);
break;
case 'images':
addFileToList(imagesEl, fileName);
break;
default:
addFileToList(otherEl, fileName);
}
});
let info = `External: ${files.length} file(s) dropped`;
if (details) {
info += ` at (${details.X}, ${details.Y})`;
}
dropDetails.textContent = info;
});
// Add drag event listeners to show coordinates during drag
document.addEventListener('dragover', (e) => {
e.preventDefault();
const debugInfo = `[DRAGOVER]
Mouse: ${e.clientX}, ${e.clientY}
Page: ${e.pageX}, ${e.pageY}
Screen: ${e.screenX}, ${e.screenY}
Window: ${windowInfo.width}x${windowInfo.height}
Target: ${e.target.tagName} ${e.target.className}`;
updateDebugOverlay(debugInfo);
});
// ===== Internal Drag and Drop =====
const draggableItems = document.querySelectorAll('.draggable-item');
const dropZones = document.querySelectorAll('.internal-drop-zone');
document.addEventListener('drop', (e) => {
e.preventDefault();
const rect = e.target.getBoundingClientRect();
const debugInfo = `[DROP EVENT]
Client: ${e.clientX}, ${e.clientY}
Page: ${e.pageX}, ${e.pageY}
Screen: ${e.screenX}, ${e.screenY}
Target rect: ${rect.left}, ${rect.top}, ${rect.width}x${rect.height}
Relative to target: ${e.clientX - rect.left}, ${e.clientY - rect.top}
Window: ${windowInfo.width}x${windowInfo.height}`;
let draggedItem = null;
draggableItems.forEach(item => {
item.addEventListener('dragstart', (e) => {
draggedItem = item;
item.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', item.dataset.task);
});
updateDebugOverlay(debugInfo);
console.log('Drop event debug info:', {
clientX: e.clientX,
clientY: e.clientY,
pageX: e.pageX,
pageY: e.pageY,
screenX: e.screenX,
screenY: e.screenY,
targetRect: rect,
relativeX: e.clientX - rect.left,
relativeY: e.clientY - rect.top
item.addEventListener('dragend', () => {
item.classList.remove('dragging');
draggedItem = null;
});
});
window.addEventListener('resize', () => {
windowInfo.width = window.innerWidth;
windowInfo.height = window.innerHeight;
});
// Update path display when clicking folders
folderNodes.forEach(folder => {
folder.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent event bubbling
const path = folder.getAttribute('data-path');
if (path) {
pathDisplay.textContent = path;
outputDiv.textContent = `Selected folder: ${path}\nReady to receive files...`;
dropZones.forEach(zone => {
zone.addEventListener('dragenter', (e) => {
// Ignore external file drags - only respond to internal HTML drags
if (e.dataTransfer?.types.includes('Files')) return;
e.preventDefault();
zone.classList.add('drag-over');
});
zone.addEventListener('dragover', (e) => {
// Ignore external file drags - only respond to internal HTML drags
if (e.dataTransfer?.types.includes('Files')) return;
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
});
zone.addEventListener('dragleave', (e) => {
// Ignore external file drags - only respond to internal HTML drags
if (e.dataTransfer?.types.includes('Files')) return;
// Only remove if leaving the zone entirely
if (!zone.contains(e.relatedTarget)) {
zone.classList.remove('drag-over');
}
});
zone.addEventListener('drop', (e) => {
// Ignore external file drags - only respond to internal HTML drags
if (e.dataTransfer?.types.includes('Files')) return;
e.preventDefault();
zone.classList.remove('drag-over');
if (draggedItem) {
const taskText = draggedItem.textContent;
const priority = zone.dataset.priority;
// Add to drop zone
const li = document.createElement('li');
li.textContent = taskText;
zone.querySelector('ul').appendChild(li);
// Remove from source
draggedItem.remove();
dropDetails.textContent = `Internal: "${taskText}" moved to ${priority} priority`;
}
});
});
// Listen for the file drop event from Wails
wails.Events.On("frontend:FileDropInfo", (eventData) => {
console.log("=============== Frontend: File Drop Debug Info ===============");
console.log("Full event data:", eventData);
const { files, targetID, targetClasses, dropX, dropY, attributes } = eventData.data[0];
console.log("Extracted data:", {
files,
targetID,
targetClasses,
dropX,
dropY,
attributes
});
// Get additional folder information from the attributes
const folderPath = attributes ? attributes['data-path'] : 'Unknown path';
const folderName = attributes ? attributes['data-folder-name'] : 'Unknown folder';
let message = `=============== FILE DROP DEBUG REPORT ===============\n`;
message += `Files dropped on folder: ${folderName}\n`;
message += `Target path: ${folderPath}\n`;
message += `Element ID: ${targetID || 'N/A'}\n`;
message += `Element Classes: ${targetClasses && targetClasses.length > 0 ? targetClasses.join(', ') : 'N/A'}\n`;
message += `\n=== COORDINATE DEBUG INFO ===\n`;
message += `Drop Coordinates from Wails: X=${dropX.toFixed(2)}, Y=${dropY.toFixed(2)}\n`;
message += `Current Mouse Position: X=${mouseInfo.x}, Y=${mouseInfo.y}\n`;
message += `Window Size: ${windowInfo.width}x${windowInfo.height}\n`;
// Get the target element to show its position
const targetElement = document.querySelector(`[data-folder-id="${targetID}"]`);
if (targetElement) {
const rect = targetElement.getBoundingClientRect();
message += `Target Element Position:\n`;
message += ` - Bounding rect: left=${rect.left}, top=${rect.top}, right=${rect.right}, bottom=${rect.bottom}\n`;
message += ` - Size: ${rect.width}x${rect.height}\n`;
message += ` - Center: ${rect.left + rect.width/2}, ${rect.top + rect.height/2}\n`;
message += ` - Drop relative to element: X=${dropX - rect.left}, Y=${dropY - rect.top}\n`;
}
message += `\n=== ELEMENT ATTRIBUTES ===\n`;
if (attributes && Object.keys(attributes).length > 0) {
Object.entries(attributes).forEach(([key, value]) => {
message += ` ${key}: "${value}"\n`;
});
} else {
message += " No attributes found\n";
}
message += `\n=== DROPPED FILES ===\n`;
files.forEach((file, index) => {
message += ` ${index + 1}. ${file}\n`;
});
message += `\n=== SIMULATION ===\n`;
message += `Simulating upload to ${folderPath}...\n`;
message += `===========================================`;
outputDiv.textContent = message;
console.log("=============== End Frontend Debug ===============");
});
console.log("File Tree Drag-and-Drop example initialized with enhanced debugging.");
createDebugOverlay();
</script>
</body>
</html>
</html>

View file

@ -1,10 +1,7 @@
package main
import (
"context"
"embed"
_ "embed"
"fmt"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
@ -14,153 +11,50 @@ import (
//go:embed assets
var assets embed.FS
// App struct
type App struct {
ctx context.Context
app *application.App
}
// NewApp creates a new App application struct
func NewApp() *App {
return &App{}
}
// Startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) Startup(ctx context.Context) {
a.ctx = ctx
a.app = application.Get()
}
// FileDropInfo defines the payload for the file drop event sent to the frontend.
type FileDropInfo struct {
Files []string `json:"files"`
TargetID string `json:"targetID"`
TargetClasses []string `json:"targetClasses"`
DropX float64 `json:"dropX"`
DropY float64 `json:"dropY"`
Attributes map[string]string `json:"attributes,omitempty"`
}
// FilesDroppedOnTarget is called when files are dropped onto a registered drop target
// or the window if no specific target is hit.
func FilesDroppedOnTarget(
files []string,
targetID string,
targetClasses []string,
dropX float64,
dropY float64,
isTargetDropzone bool, // This parameter is kept for logging but not sent to frontend in this event
attributes map[string]string,
) {
log.Println("=============== Go: FilesDroppedOnTarget Debug Info ===============")
log.Println(fmt.Sprintf(" Files: %v", files))
log.Println(fmt.Sprintf(" Target ID: '%s'", targetID))
log.Println(fmt.Sprintf(" Target Classes: %v", targetClasses))
log.Println(fmt.Sprintf(" Drop X: %f, Drop Y: %f", dropX, dropY))
log.Println(
fmt.Sprintf(
" Drop occurred on a designated dropzone (runtime validated before this Go event): %t",
isTargetDropzone,
),
)
log.Println(fmt.Sprintf(" Element Attributes: %v", attributes))
log.Println("================================================================")
payload := FileDropInfo{
Files: files,
TargetID: targetID,
TargetClasses: targetClasses,
DropX: dropX,
DropY: dropY,
Attributes: attributes,
}
log.Println("Go: Emitted 'frontend:FileDropInfo' event with payload:", payload)
}
func main() {
appInstance := NewApp()
app := application.New(application.Options{
Name: "Drag-n-drop Demo",
Description: "A demo of the Drag-n-drop API",
Name: "File Drop Demo",
Description: "A demo of file drag and drop",
Assets: application.AssetOptions{
Handler: application.BundledAssetFileServer(assets),
},
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
Services: []application.Service{
application.NewService(appInstance),
},
})
win := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Drag-n-drop Demo",
Title: "File Drop Demo",
Width: 800,
Height: 600,
EnableFileDrop: true,
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified,
InvisibleTitleBarHeight: 50,
},
EnableDragAndDrop: true,
})
log.Println("Setting up event listener for 'WindowDropZoneFilesDropped'...")
win.OnWindowEvent(
events.Common.WindowDropZoneFilesDropped,
func(event *application.WindowEvent) {
// Listen for file drop events
win.OnWindowEvent(events.Common.WindowFilesDropped, func(event *application.WindowEvent) {
files := event.Context().DroppedFiles()
details := event.Context().DropTargetDetails()
droppedFiles := event.Context().DroppedFiles()
details := event.Context().DropZoneDetails()
log.Printf("Files dropped: %v", files)
if details != nil {
log.Printf("Drop target: id=%s, classes=%v, x=%d, y=%d",
details.ElementID, details.ClassList, details.X, details.Y)
}
log.Printf("Dropped files count: %d", len(droppedFiles))
log.Printf("Event context: %+v", event.Context())
if details != nil {
log.Printf("DropZone details found:")
log.Printf(" ElementID: '%s'", details.ElementID)
log.Printf(" ClassList: %v", details.ClassList)
log.Printf(" X: %d, Y: %d", details.X, details.Y)
log.Printf(" Attributes: %+v", details.Attributes)
// Call the App method with the extracted data
FilesDroppedOnTarget(
droppedFiles,
details.ElementID,
details.ClassList,
float64(details.X),
float64(details.Y),
details.ElementID != "", // isTargetDropzone based on whether an ID was found
details.Attributes,
)
} else {
log.Println("DropZone details are nil - drop was not on a specific registered zone")
// This case might occur if DropZoneDetails are nil, meaning the drop was not on a specific registered zone
// or if the context itself was problematic.
FilesDroppedOnTarget(droppedFiles, "", nil, 0, 0, false, nil)
}
payload := FileDropInfo{
Files: droppedFiles,
TargetID: details.ElementID,
TargetClasses: details.ClassList,
DropX: float64(details.X),
DropY: float64(details.Y),
Attributes: details.Attributes, // Add the attributes
}
log.Printf("Emitting event payload: %+v", payload)
application.Get().Event.Emit("frontend:FileDropInfo", payload)
log.Println(
"=============== End WindowDropZoneFilesDropped Event Debug ===============",
)
},
)
// Emit event to frontend
application.Get().Event.Emit("files-dropped", map[string]any{
"files": files,
"details": details,
})
})
err := app.Run()
if err != nil {
log.Fatal(err.Error())
log.Fatal(err)
}
}

View file

@ -1,43 +0,0 @@
# HTML Drag and Drop API Example
This example should demonstrate whether the [HTML Drag and Drop API](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API") works correctly.
## Expected Behaviour
When dragging the "draggable", in the console should be printed:
1. "dragstart" once
2. "drag" many times
3. "dragend" once
When dragging the "draggable" on the drop target, the inner text of the latter shoud change and in the console should be printed:
1. "dragstart" once
2. "drag" many times
3. "dragenter" once
4. "dragover" many times (alternating with "drag")
5. - "drop" once (in case of a drop inside the drop target)
- "dragleave" once (in case the draggable div leaves the drop target)
6. "dragend" once
## Running the example
To run the example, simply run the following command:
```bash
go run main.go
```
## Building the example
To build the example in debug mode, simply run the following command:
```bash
wails3 task build
```
# Status
| Platform | Status |
|----------|-------------|
| Mac | Working |
| Windows | Not Working |
| Linux | |

View file

@ -1,75 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
background-color: white;
}
#draggable {
width: 100px;
height: 100px;
background-color: yellow;
text-align: center;
}
#dropTarget {
width: 200px;
height: 200px;
border: 2px solid red;
text-align: center;
}
</style>
</head>
<body>
<h1>HTML Drag and Drop API Demo</h1>
<br/>
<div id="draggable" draggable="true" >draggable</div>
<div id="dropTarget" >drop target</div>
</body>
<script type="module">
const draggable = document.getElementById('draggable');
draggable.addEventListener('dragstart', (event) => {
console.log('dragstart');
dropTarget.innerText = 'drop target';
});
draggable.addEventListener("drag", (event) => {
console.log('drag');
});
draggable.addEventListener("dragend", (event) => {
console.log('dragend');
});
const dropTarget = document.getElementById('dropTarget');
dropTarget.addEventListener('dragenter', (event) => {
console.log('dragenter');
});
dropTarget.addEventListener('dragleave', (event) => {
console.log('dragleave');
dropTarget.innerText = 'left drop target';
});
dropTarget.addEventListener('dragover', (event) => {
event.preventDefault()
console.log('dragover');
dropTarget.innerText = 'dragged over';
});
dropTarget.addEventListener('drop', (event) => {
console.log('drop');
dropTarget.innerText = 'dropped';
});
</script>
</html>

View file

@ -1,40 +0,0 @@
package main
import (
"embed"
"log"
"github.com/wailsapp/wails/v3/pkg/application"
)
//go:embed assets
var assets embed.FS
func main() {
app := application.New(application.Options{
Name: "HTML Drag and Drop API Demo",
Description: "A demo of the HTML Drag and drop API",
Assets: application.AssetOptions{
Handler: application.BundledAssetFileServer(assets),
},
Mac: application.MacOptions{
ApplicationShouldTerminateAfterLastWindowClosed: true,
},
})
app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Drag-n-drop Demo",
Mac: application.MacWindow{
Backdrop: application.MacBackdropTranslucent,
TitleBar: application.MacTitleBarHiddenInsetUnified,
InvisibleTitleBarHeight: 50,
},
})
err := app.Run()
if err != nil {
log.Fatal(err.Error())
}
}

View file

@ -61,15 +61,15 @@ func main() {
// Window 1: Light style with no tint
window1 := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Light Glass",
Width: 350,
Height: 280,
X: 100,
Y: 100,
Frameless: true,
EnableDragAndDrop: false,
HTML: lightHTML,
InitialPosition: application.WindowXY,
Title: "Light Glass",
Width: 350,
Height: 280,
X: 100,
Y: 100,
Frameless: true,
EnableFileDrop: false,
HTML: lightHTML,
InitialPosition: application.WindowXY,
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
InvisibleTitleBarHeight: 500,
@ -84,15 +84,15 @@ func main() {
// Window 2: Dark style
window2 := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Dark Glass",
Width: 350,
Height: 280,
X: 500,
Y: 100,
Frameless: true,
EnableDragAndDrop: false,
HTML: darkHTML,
InitialPosition: application.WindowXY,
Title: "Dark Glass",
Width: 350,
Height: 280,
X: 500,
Y: 100,
Frameless: true,
EnableFileDrop: false,
HTML: darkHTML,
InitialPosition: application.WindowXY,
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
InvisibleTitleBarHeight: 500,
@ -107,15 +107,15 @@ func main() {
// Window 3: Vibrant style
window3 := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Vibrant Glass",
Width: 350,
Height: 280,
X: 900,
Y: 100,
Frameless: true,
EnableDragAndDrop: false,
HTML: vibrantHTML,
InitialPosition: application.WindowXY,
Title: "Vibrant Glass",
Width: 350,
Height: 280,
X: 900,
Y: 100,
Frameless: true,
EnableFileDrop: false,
HTML: vibrantHTML,
InitialPosition: application.WindowXY,
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
InvisibleTitleBarHeight: 500,
@ -130,15 +130,15 @@ func main() {
// Window 4: Blue tinted glass
window4 := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Tinted Glass",
Width: 350,
Height: 280,
X: 300,
Y: 420,
Frameless: true,
EnableDragAndDrop: false,
HTML: tintedHTML,
InitialPosition: application.WindowXY,
Title: "Tinted Glass",
Width: 350,
Height: 280,
X: 300,
Y: 420,
Frameless: true,
EnableFileDrop: false,
HTML: tintedHTML,
InitialPosition: application.WindowXY,
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
InvisibleTitleBarHeight: 500,
@ -153,15 +153,15 @@ func main() {
// Window 5: Using specific NSVisualEffectMaterial
window5 := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Sheet Material",
Width: 350,
Height: 280,
X: 700,
Y: 420,
Frameless: true,
EnableDragAndDrop: false,
HTML: sheetHTML,
InitialPosition: application.WindowXY,
Title: "Sheet Material",
Width: 350,
Height: 280,
X: 700,
Y: 420,
Frameless: true,
EnableFileDrop: false,
HTML: sheetHTML,
InitialPosition: application.WindowXY,
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
InvisibleTitleBarHeight: 500,
@ -176,15 +176,15 @@ func main() {
// Window 6: HUD Window Material (very light, translucent)
window6 := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "HUD Window",
Width: 350,
Height: 280,
X: 100,
Y: 740,
Frameless: true,
EnableDragAndDrop: false,
HTML: hudHTML,
InitialPosition: application.WindowXY,
Title: "HUD Window",
Width: 350,
Height: 280,
X: 100,
Y: 740,
Frameless: true,
EnableFileDrop: false,
HTML: hudHTML,
InitialPosition: application.WindowXY,
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
InvisibleTitleBarHeight: 500,
@ -199,15 +199,15 @@ func main() {
// Window 7: Content Background Material
window7 := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Content Background",
Width: 350,
Height: 280,
X: 500,
Y: 740,
Frameless: true,
EnableDragAndDrop: false,
HTML: contentHTML,
InitialPosition: application.WindowXY,
Title: "Content Background",
Width: 350,
Height: 280,
X: 500,
Y: 740,
Frameless: true,
EnableFileDrop: false,
HTML: contentHTML,
InitialPosition: application.WindowXY,
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
InvisibleTitleBarHeight: 500,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -6,218 +6,217 @@ func IsKnownEvent(name string) bool {
}
var knownEvents = map[string]struct{}{
"common:ApplicationOpenedWithFile": {},
"common:ApplicationStarted": {},
"common:ApplicationLaunchedWithUrl": {},
"common:ThemeChanged": {},
"common:WindowClosing": {},
"common:WindowDidMove": {},
"common:WindowDidResize": {},
"common:WindowDPIChanged": {},
"common:WindowFilesDropped": {},
"common:WindowFocus": {},
"common:WindowFullscreen": {},
"common:WindowHide": {},
"common:WindowLostFocus": {},
"common:WindowMaximise": {},
"common:WindowMinimise": {},
"common:WindowToggleFrameless": {},
"common:WindowRestore": {},
"common:WindowRuntimeReady": {},
"common:WindowShow": {},
"common:WindowUnFullscreen": {},
"common:WindowUnMaximise": {},
"common:WindowUnMinimise": {},
"common:WindowZoom": {},
"common:WindowZoomIn": {},
"common:WindowZoomOut": {},
"common:WindowZoomReset": {},
"common:WindowDropZoneFilesDropped": {},
"linux:ApplicationStartup": {},
"linux:SystemThemeChanged": {},
"linux:WindowDeleteEvent": {},
"linux:WindowDidMove": {},
"linux:WindowDidResize": {},
"linux:WindowFocusIn": {},
"linux:WindowFocusOut": {},
"linux:WindowLoadStarted": {},
"linux:WindowLoadRedirected": {},
"linux:WindowLoadCommitted": {},
"linux:WindowLoadFinished": {},
"mac:ApplicationDidBecomeActive": {},
"mac:ApplicationDidChangeBackingProperties": {},
"mac:ApplicationDidChangeEffectiveAppearance": {},
"mac:ApplicationDidChangeIcon": {},
"mac:ApplicationDidChangeOcclusionState": {},
"mac:ApplicationDidChangeScreenParameters": {},
"mac:ApplicationDidChangeStatusBarFrame": {},
"mac:ApplicationDidChangeStatusBarOrientation": {},
"mac:ApplicationDidChangeTheme": {},
"mac:ApplicationDidFinishLaunching": {},
"mac:ApplicationDidHide": {},
"mac:ApplicationDidResignActive": {},
"mac:ApplicationDidUnhide": {},
"mac:ApplicationDidUpdate": {},
"mac:ApplicationShouldHandleReopen": {},
"mac:ApplicationWillBecomeActive": {},
"mac:ApplicationWillFinishLaunching": {},
"mac:ApplicationWillHide": {},
"mac:ApplicationWillResignActive": {},
"mac:ApplicationWillTerminate": {},
"mac:ApplicationWillUnhide": {},
"mac:ApplicationWillUpdate": {},
"mac:MenuDidAddItem": {},
"mac:MenuDidBeginTracking": {},
"mac:MenuDidClose": {},
"mac:MenuDidDisplayItem": {},
"mac:MenuDidEndTracking": {},
"mac:MenuDidHighlightItem": {},
"mac:MenuDidOpen": {},
"mac:MenuDidPopUp": {},
"mac:MenuDidRemoveItem": {},
"mac:MenuDidSendAction": {},
"mac:MenuDidSendActionToItem": {},
"mac:MenuDidUpdate": {},
"mac:MenuWillAddItem": {},
"mac:MenuWillBeginTracking": {},
"mac:MenuWillDisplayItem": {},
"mac:MenuWillEndTracking": {},
"mac:MenuWillHighlightItem": {},
"mac:MenuWillOpen": {},
"mac:MenuWillPopUp": {},
"mac:MenuWillRemoveItem": {},
"mac:MenuWillSendAction": {},
"mac:MenuWillSendActionToItem": {},
"mac:MenuWillUpdate": {},
"mac:WebViewDidCommitNavigation": {},
"mac:WebViewDidFinishNavigation": {},
"common:ApplicationOpenedWithFile": {},
"common:ApplicationStarted": {},
"common:ApplicationLaunchedWithUrl": {},
"common:ThemeChanged": {},
"common:WindowClosing": {},
"common:WindowDidMove": {},
"common:WindowDidResize": {},
"common:WindowDPIChanged": {},
"common:WindowFilesDropped": {},
"common:WindowFocus": {},
"common:WindowFullscreen": {},
"common:WindowHide": {},
"common:WindowLostFocus": {},
"common:WindowMaximise": {},
"common:WindowMinimise": {},
"common:WindowToggleFrameless": {},
"common:WindowRestore": {},
"common:WindowRuntimeReady": {},
"common:WindowShow": {},
"common:WindowUnFullscreen": {},
"common:WindowUnMaximise": {},
"common:WindowUnMinimise": {},
"common:WindowZoom": {},
"common:WindowZoomIn": {},
"common:WindowZoomOut": {},
"common:WindowZoomReset": {},
"linux:ApplicationStartup": {},
"linux:SystemThemeChanged": {},
"linux:WindowDeleteEvent": {},
"linux:WindowDidMove": {},
"linux:WindowDidResize": {},
"linux:WindowFocusIn": {},
"linux:WindowFocusOut": {},
"linux:WindowLoadStarted": {},
"linux:WindowLoadRedirected": {},
"linux:WindowLoadCommitted": {},
"linux:WindowLoadFinished": {},
"mac:ApplicationDidBecomeActive": {},
"mac:ApplicationDidChangeBackingProperties": {},
"mac:ApplicationDidChangeEffectiveAppearance": {},
"mac:ApplicationDidChangeIcon": {},
"mac:ApplicationDidChangeOcclusionState": {},
"mac:ApplicationDidChangeScreenParameters": {},
"mac:ApplicationDidChangeStatusBarFrame": {},
"mac:ApplicationDidChangeStatusBarOrientation": {},
"mac:ApplicationDidChangeTheme": {},
"mac:ApplicationDidFinishLaunching": {},
"mac:ApplicationDidHide": {},
"mac:ApplicationDidResignActive": {},
"mac:ApplicationDidUnhide": {},
"mac:ApplicationDidUpdate": {},
"mac:ApplicationShouldHandleReopen": {},
"mac:ApplicationWillBecomeActive": {},
"mac:ApplicationWillFinishLaunching": {},
"mac:ApplicationWillHide": {},
"mac:ApplicationWillResignActive": {},
"mac:ApplicationWillTerminate": {},
"mac:ApplicationWillUnhide": {},
"mac:ApplicationWillUpdate": {},
"mac:MenuDidAddItem": {},
"mac:MenuDidBeginTracking": {},
"mac:MenuDidClose": {},
"mac:MenuDidDisplayItem": {},
"mac:MenuDidEndTracking": {},
"mac:MenuDidHighlightItem": {},
"mac:MenuDidOpen": {},
"mac:MenuDidPopUp": {},
"mac:MenuDidRemoveItem": {},
"mac:MenuDidSendAction": {},
"mac:MenuDidSendActionToItem": {},
"mac:MenuDidUpdate": {},
"mac:MenuWillAddItem": {},
"mac:MenuWillBeginTracking": {},
"mac:MenuWillDisplayItem": {},
"mac:MenuWillEndTracking": {},
"mac:MenuWillHighlightItem": {},
"mac:MenuWillOpen": {},
"mac:MenuWillPopUp": {},
"mac:MenuWillRemoveItem": {},
"mac:MenuWillSendAction": {},
"mac:MenuWillSendActionToItem": {},
"mac:MenuWillUpdate": {},
"mac:WebViewDidCommitNavigation": {},
"mac:WebViewDidFinishNavigation": {},
"mac:WebViewDidReceiveServerRedirectForProvisionalNavigation": {},
"mac:WebViewDidStartProvisionalNavigation": {},
"mac:WindowDidBecomeKey": {},
"mac:WindowDidBecomeMain": {},
"mac:WindowDidBeginSheet": {},
"mac:WindowDidChangeAlpha": {},
"mac:WindowDidChangeBackingLocation": {},
"mac:WindowDidChangeBackingProperties": {},
"mac:WindowDidChangeCollectionBehavior": {},
"mac:WindowDidChangeEffectiveAppearance": {},
"mac:WindowDidChangeOcclusionState": {},
"mac:WindowDidChangeOrderingMode": {},
"mac:WindowDidChangeScreen": {},
"mac:WindowDidChangeScreenParameters": {},
"mac:WindowDidChangeScreenProfile": {},
"mac:WindowDidChangeScreenSpace": {},
"mac:WindowDidChangeScreenSpaceProperties": {},
"mac:WindowDidChangeSharingType": {},
"mac:WindowDidChangeSpace": {},
"mac:WindowDidChangeSpaceOrderingMode": {},
"mac:WindowDidChangeTitle": {},
"mac:WindowDidChangeToolbar": {},
"mac:WindowDidDeminiaturize": {},
"mac:WindowDidEndSheet": {},
"mac:WindowDidEnterFullScreen": {},
"mac:WindowDidEnterVersionBrowser": {},
"mac:WindowDidExitFullScreen": {},
"mac:WindowDidExitVersionBrowser": {},
"mac:WindowDidExpose": {},
"mac:WindowDidFocus": {},
"mac:WindowDidMiniaturize": {},
"mac:WindowDidMove": {},
"mac:WindowDidOrderOffScreen": {},
"mac:WindowDidOrderOnScreen": {},
"mac:WindowDidResignKey": {},
"mac:WindowDidResignMain": {},
"mac:WindowDidResize": {},
"mac:WindowDidUpdate": {},
"mac:WindowDidUpdateAlpha": {},
"mac:WindowDidUpdateCollectionBehavior": {},
"mac:WindowDidUpdateCollectionProperties": {},
"mac:WindowDidUpdateShadow": {},
"mac:WindowDidUpdateTitle": {},
"mac:WindowDidUpdateToolbar": {},
"mac:WindowDidZoom": {},
"mac:WindowFileDraggingEntered": {},
"mac:WindowFileDraggingExited": {},
"mac:WindowFileDraggingPerformed": {},
"mac:WindowHide": {},
"mac:WindowMaximise": {},
"mac:WindowUnMaximise": {},
"mac:WindowMinimise": {},
"mac:WindowUnMinimise": {},
"mac:WindowShouldClose": {},
"mac:WindowShow": {},
"mac:WindowWillBecomeKey": {},
"mac:WindowWillBecomeMain": {},
"mac:WindowWillBeginSheet": {},
"mac:WindowWillChangeOrderingMode": {},
"mac:WindowWillClose": {},
"mac:WindowWillDeminiaturize": {},
"mac:WindowWillEnterFullScreen": {},
"mac:WindowWillEnterVersionBrowser": {},
"mac:WindowWillExitFullScreen": {},
"mac:WindowWillExitVersionBrowser": {},
"mac:WindowWillFocus": {},
"mac:WindowWillMiniaturize": {},
"mac:WindowWillMove": {},
"mac:WindowWillOrderOffScreen": {},
"mac:WindowWillOrderOnScreen": {},
"mac:WindowWillResignMain": {},
"mac:WindowWillResize": {},
"mac:WindowWillUnfocus": {},
"mac:WindowWillUpdate": {},
"mac:WindowWillUpdateAlpha": {},
"mac:WindowWillUpdateCollectionBehavior": {},
"mac:WindowWillUpdateCollectionProperties": {},
"mac:WindowWillUpdateShadow": {},
"mac:WindowWillUpdateTitle": {},
"mac:WindowWillUpdateToolbar": {},
"mac:WindowWillUpdateVisibility": {},
"mac:WindowWillUseStandardFrame": {},
"mac:WindowZoomIn": {},
"mac:WindowZoomOut": {},
"mac:WindowZoomReset": {},
"windows:APMPowerSettingChange": {},
"windows:APMPowerStatusChange": {},
"windows:APMResumeAutomatic": {},
"windows:APMResumeSuspend": {},
"windows:APMSuspend": {},
"windows:ApplicationStarted": {},
"windows:SystemThemeChanged": {},
"windows:WebViewNavigationCompleted": {},
"windows:WindowActive": {},
"windows:WindowBackgroundErase": {},
"windows:WindowClickActive": {},
"windows:WindowClosing": {},
"windows:WindowDidMove": {},
"windows:WindowDidResize": {},
"windows:WindowDPIChanged": {},
"windows:WindowDragDrop": {},
"windows:WindowDragEnter": {},
"windows:WindowDragLeave": {},
"windows:WindowDragOver": {},
"windows:WindowEndMove": {},
"windows:WindowEndResize": {},
"windows:WindowFullscreen": {},
"windows:WindowHide": {},
"windows:WindowInactive": {},
"windows:WindowKeyDown": {},
"windows:WindowKeyUp": {},
"windows:WindowKillFocus": {},
"windows:WindowNonClientHit": {},
"windows:WindowNonClientMouseDown": {},
"windows:WindowNonClientMouseLeave": {},
"windows:WindowNonClientMouseMove": {},
"windows:WindowNonClientMouseUp": {},
"windows:WindowPaint": {},
"windows:WindowRestore": {},
"windows:WindowSetFocus": {},
"windows:WindowShow": {},
"windows:WindowStartMove": {},
"windows:WindowStartResize": {},
"windows:WindowUnFullscreen": {},
"windows:WindowZOrderChanged": {},
"windows:WindowMinimise": {},
"windows:WindowUnMinimise": {},
"windows:WindowMaximise": {},
"windows:WindowUnMaximise": {},
"mac:WebViewDidStartProvisionalNavigation": {},
"mac:WindowDidBecomeKey": {},
"mac:WindowDidBecomeMain": {},
"mac:WindowDidBeginSheet": {},
"mac:WindowDidChangeAlpha": {},
"mac:WindowDidChangeBackingLocation": {},
"mac:WindowDidChangeBackingProperties": {},
"mac:WindowDidChangeCollectionBehavior": {},
"mac:WindowDidChangeEffectiveAppearance": {},
"mac:WindowDidChangeOcclusionState": {},
"mac:WindowDidChangeOrderingMode": {},
"mac:WindowDidChangeScreen": {},
"mac:WindowDidChangeScreenParameters": {},
"mac:WindowDidChangeScreenProfile": {},
"mac:WindowDidChangeScreenSpace": {},
"mac:WindowDidChangeScreenSpaceProperties": {},
"mac:WindowDidChangeSharingType": {},
"mac:WindowDidChangeSpace": {},
"mac:WindowDidChangeSpaceOrderingMode": {},
"mac:WindowDidChangeTitle": {},
"mac:WindowDidChangeToolbar": {},
"mac:WindowDidDeminiaturize": {},
"mac:WindowDidEndSheet": {},
"mac:WindowDidEnterFullScreen": {},
"mac:WindowDidEnterVersionBrowser": {},
"mac:WindowDidExitFullScreen": {},
"mac:WindowDidExitVersionBrowser": {},
"mac:WindowDidExpose": {},
"mac:WindowDidFocus": {},
"mac:WindowDidMiniaturize": {},
"mac:WindowDidMove": {},
"mac:WindowDidOrderOffScreen": {},
"mac:WindowDidOrderOnScreen": {},
"mac:WindowDidResignKey": {},
"mac:WindowDidResignMain": {},
"mac:WindowDidResize": {},
"mac:WindowDidUpdate": {},
"mac:WindowDidUpdateAlpha": {},
"mac:WindowDidUpdateCollectionBehavior": {},
"mac:WindowDidUpdateCollectionProperties": {},
"mac:WindowDidUpdateShadow": {},
"mac:WindowDidUpdateTitle": {},
"mac:WindowDidUpdateToolbar": {},
"mac:WindowDidZoom": {},
"mac:WindowFileDraggingEntered": {},
"mac:WindowFileDraggingExited": {},
"mac:WindowFileDraggingPerformed": {},
"mac:WindowHide": {},
"mac:WindowMaximise": {},
"mac:WindowUnMaximise": {},
"mac:WindowMinimise": {},
"mac:WindowUnMinimise": {},
"mac:WindowShouldClose": {},
"mac:WindowShow": {},
"mac:WindowWillBecomeKey": {},
"mac:WindowWillBecomeMain": {},
"mac:WindowWillBeginSheet": {},
"mac:WindowWillChangeOrderingMode": {},
"mac:WindowWillClose": {},
"mac:WindowWillDeminiaturize": {},
"mac:WindowWillEnterFullScreen": {},
"mac:WindowWillEnterVersionBrowser": {},
"mac:WindowWillExitFullScreen": {},
"mac:WindowWillExitVersionBrowser": {},
"mac:WindowWillFocus": {},
"mac:WindowWillMiniaturize": {},
"mac:WindowWillMove": {},
"mac:WindowWillOrderOffScreen": {},
"mac:WindowWillOrderOnScreen": {},
"mac:WindowWillResignMain": {},
"mac:WindowWillResize": {},
"mac:WindowWillUnfocus": {},
"mac:WindowWillUpdate": {},
"mac:WindowWillUpdateAlpha": {},
"mac:WindowWillUpdateCollectionBehavior": {},
"mac:WindowWillUpdateCollectionProperties": {},
"mac:WindowWillUpdateShadow": {},
"mac:WindowWillUpdateTitle": {},
"mac:WindowWillUpdateToolbar": {},
"mac:WindowWillUpdateVisibility": {},
"mac:WindowWillUseStandardFrame": {},
"mac:WindowZoomIn": {},
"mac:WindowZoomOut": {},
"mac:WindowZoomReset": {},
"windows:APMPowerSettingChange": {},
"windows:APMPowerStatusChange": {},
"windows:APMResumeAutomatic": {},
"windows:APMResumeSuspend": {},
"windows:APMSuspend": {},
"windows:ApplicationStarted": {},
"windows:SystemThemeChanged": {},
"windows:WebViewNavigationCompleted": {},
"windows:WindowActive": {},
"windows:WindowBackgroundErase": {},
"windows:WindowClickActive": {},
"windows:WindowClosing": {},
"windows:WindowDidMove": {},
"windows:WindowDidResize": {},
"windows:WindowDPIChanged": {},
"windows:WindowDragDrop": {},
"windows:WindowDragEnter": {},
"windows:WindowDragLeave": {},
"windows:WindowDragOver": {},
"windows:WindowEndMove": {},
"windows:WindowEndResize": {},
"windows:WindowFullscreen": {},
"windows:WindowHide": {},
"windows:WindowInactive": {},
"windows:WindowKeyDown": {},
"windows:WindowKeyUp": {},
"windows:WindowKillFocus": {},
"windows:WindowNonClientHit": {},
"windows:WindowNonClientMouseDown": {},
"windows:WindowNonClientMouseLeave": {},
"windows:WindowNonClientMouseMove": {},
"windows:WindowNonClientMouseUp": {},
"windows:WindowPaint": {},
"windows:WindowRestore": {},
"windows:WindowSetFocus": {},
"windows:WindowShow": {},
"windows:WindowStartMove": {},
"windows:WindowStartResize": {},
"windows:WindowUnFullscreen": {},
"windows:WindowZOrderChanged": {},
"windows:WindowMinimise": {},
"windows:WindowUnMinimise": {},
"windows:WindowMaximise": {},
"windows:WindowUnMaximise": {},
}

View file

@ -35,14 +35,38 @@ tasks:
cmds:
- npx esbuild@latest desktop/@wailsio/runtime/src/index.ts --inject:desktop/compiled/main.js --format=esm --target=safari11 --bundle --ignore-annotations --tree-shaking=true --minify --outfile=../assetserver/bundledassets/runtime.js --define:DEBUG=false --drop:console
build:all:
build:docs:
internal: true
dir: desktop/@wailsio/runtime
deps:
- install-deps
cmds:
- npm run build:docs
build:docs:md:
internal: true
dir: desktop/@wailsio/runtime
deps:
- install-deps
cmds:
- npm run build:docs:md
build:runtime:
internal: true
deps:
- build:debug
- build:production
cmds:
- cmd: echo "Build Complete."
- cmd: echo "Runtime build complete."
build:all:
internal: true
cmds:
- task: generate:events
- task: build:docs
- task: build:runtime
- echo "Build Complete."
build:
deps:
@ -50,8 +74,39 @@ tasks:
cmds:
- task: build:all
docs:
summary: Generate TypeDoc documentation for the runtime
dir: desktop/@wailsio/runtime
deps:
- install-deps
cmds:
- npm run build:docs
- echo "Documentation generated at desktop/@wailsio/runtime/docs/"
docs:md:
summary: Generate markdown documentation for the runtime
dir: desktop/@wailsio/runtime
deps:
- install-deps
cmds:
- npm run build:docs:md
- echo "Markdown documentation generated"
generate:events:
dir: ../../tasks/events
cmds:
- go run generate.go
- go fmt ../../pkg/events/events.go
clean:
summary: Clean built artifacts and documentation
dir: desktop/@wailsio/runtime
cmds:
- npm run clean
- echo "Cleaned runtime artifacts"
generate:
summary: Generate events only (use runtime:build to rebuild everything)
cmds:
- task: generate:events
- echo "Events generated. Run 'wails3 task runtime:build' to rebuild runtime with updated documentation"

View file

@ -1584,6 +1584,7 @@
"integrity": "sha512-HL26ajjMVe/wr3xlzjF0sCPCiAKaZJcIRFZHmG4yKHRJp4YAkHPG5X6GfWxCeDTpOmuHhNiOyNKUoZjjnm0tjw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"webidl-conversions": "^7.0.0",
"whatwg-mimetype": "^3.0.0"
@ -2561,6 +2562,7 @@
"integrity": "sha512-/z585740YHURLl9DN2jCWe6OW7zKYm6VoQ93H0sxZ1cwHQEQrUn5BJrEnkWhfzUdyO+BLGjnKUZ9iz9hKloFDw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@gerrit0/mini-shiki": "^1.24.0",
"lunr": "^2.3.9",
@ -2633,6 +2635,7 @@
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -2661,6 +2664,7 @@
"integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",

View file

@ -256,6 +256,5 @@ export const Types = Object.freeze({
WindowZoomIn: "common:WindowZoomIn",
WindowZoomOut: "common:WindowZoomOut",
WindowZoomReset: "common:WindowZoomReset",
WindowDropZoneFilesDropped: "common:WindowDropZoneFilesDropped",
}),
});

View file

@ -26,7 +26,7 @@ import * as Flags from "./flags.js";
import * as Screens from "./screens.js";
import * as System from "./system.js";
import * as IOS from "./ios.js";
import Window from "./window.js";
import Window, { handleDragEnter, handleDragLeave, handleDragOver } from "./window.js";
import * as WML from "./wml.js";
export {
@ -70,4 +70,9 @@ window._wails.invoke = System.invoke;
// Binding ensures 'this' correctly refers to the current window instance
window._wails.handlePlatformFileDrop = Window.HandlePlatformFileDrop.bind(Window);
// Linux-specific drag handlers (GTK intercepts DOM drag events)
window._wails.handleDragEnter = handleDragEnter;
window._wails.handleDragLeave = handleDragLeave;
window._wails.handleDragOver = handleDragOver;
System.invoke("wails:runtime:ready");

View file

@ -15,7 +15,6 @@ const call = newRuntimeCaller(objectNames.System);
const SystemIsDarkMode = 0;
const SystemEnvironment = 1;
const SystemCapabilities = 2;
const ApplicationFilesDroppedWithContext = 100; // New method ID for enriched drop event
const _invoke = (function () {
try {
@ -158,35 +157,3 @@ export function IsDebug(): boolean {
return Boolean((window as any)._wails?.environment?.Debug);
}
/**
* Handles file drops originating from platform-specific code (e.g., macOS native drag-and-drop).
* Gathers information about the drop target element and sends it back to the Go backend.
*
* @param filenames - An array of file paths (strings) that were dropped.
* @param x - The x-coordinate of the drop event.
* @param y - The y-coordinate of the drop event.
*/
export function HandlePlatformFileDrop(filenames: string[], x: number, y: number): void {
const element = document.elementFromPoint(x, y);
const elementId = element ? element.id : '';
const classList = element ? Array.from(element.classList) : [];
const payload = {
filenames,
x,
y,
elementId,
classList,
};
call(ApplicationFilesDroppedWithContext, payload)
.then(() => {
// Optional: Log success or handle if needed
console.log("Platform file drop processed and sent to Go.");
})
.catch(err => {
// Optional: Log error
console.error("Error sending platform file drop to Go:", err);
});
}

View file

@ -11,10 +11,10 @@ The electron alternative for Go
import {newRuntimeCaller, objectNames} from "./runtime.js";
import type { Screen } from "./screens.js";
// NEW: Dropzone constants
const DROPZONE_ATTRIBUTE = 'data-wails-dropzone';
const DROPZONE_HOVER_CLASS = 'wails-dropzone-hover'; // User can style this class
let currentHoveredDropzone: Element | null = null;
// Drop target constants
const DROP_TARGET_ATTRIBUTE = 'data-file-drop-target';
const DROP_TARGET_ACTIVE_CLASS = 'file-drop-target-active';
let currentDropTarget: Element | null = null;
const PositionMethod = 0;
const CenterMethod = 1;
@ -66,17 +66,108 @@ const ZoomInMethod = 46;
const ZoomOutMethod = 47;
const ZoomResetMethod = 48;
const SnapAssistMethod = 49;
const WindowDropZoneDropped = 50;
const FilesDropped = 50;
const PrintMethod = 51;
function getDropzoneElement(element: Element | null): Element | null {
/**
* Finds the nearest drop target element by walking up the DOM tree.
*/
function getDropTargetElement(element: Element | null): Element | null {
if (!element) {
return null;
}
// Allow dropzone attribute to be on the element itself or any parent
return element.closest(`[${DROPZONE_ATTRIBUTE}]`);
return element.closest(`[${DROP_TARGET_ATTRIBUTE}]`);
}
/**
* Check if we can use WebView2's postMessageWithAdditionalObjects (Windows)
* Also checks that EnableFileDrop is true for this window.
*/
function canResolveFilePaths(): boolean {
// Must have WebView2's postMessageWithAdditionalObjects API (Windows only)
if ((window as any).chrome?.webview?.postMessageWithAdditionalObjects == null) {
return false;
}
// Must have EnableFileDrop set to true for this window
// This flag is set by the Go backend during runtime initialization
return (window as any)._wails?.flags?.enableFileDrop === true;
}
/**
* Send file drop to backend via WebView2 (Windows only)
*/
function resolveFilePaths(x: number, y: number, files: File[]): void {
if ((window as any).chrome?.webview?.postMessageWithAdditionalObjects) {
(window as any).chrome.webview.postMessageWithAdditionalObjects(`file:drop:${x}:${y}`, files);
}
}
// Native drag state (Linux/macOS intercept DOM drag events)
let nativeDragActive = false;
/**
* Cleans up native drag state and hover effects.
* Called on drop or when drag leaves the window.
*/
function cleanupNativeDrag(): void {
nativeDragActive = false;
if (currentDropTarget) {
currentDropTarget.classList.remove(DROP_TARGET_ACTIVE_CLASS);
currentDropTarget = null;
}
}
/**
* Called from Go when a file drag enters the window on Linux/macOS.
*/
function handleDragEnter(): void {
// Check if file drops are enabled for this window
if ((window as any)._wails?.flags?.enableFileDrop === false) {
return; // File drops disabled, don't activate drag state
}
nativeDragActive = true;
}
/**
* Called from Go when a file drag leaves the window on Linux/macOS.
*/
function handleDragLeave(): void {
cleanupNativeDrag();
}
/**
* Called from Go during file drag to update hover state on Linux/macOS.
* @param x - X coordinate in CSS pixels
* @param y - Y coordinate in CSS pixels
*/
function handleDragOver(x: number, y: number): void {
if (!nativeDragActive) return;
// Check if file drops are enabled for this window
if ((window as any)._wails?.flags?.enableFileDrop === false) {
return; // File drops disabled, don't show hover effects
}
const targetElement = document.elementFromPoint(x, y);
const dropTarget = getDropTargetElement(targetElement);
if (currentDropTarget && currentDropTarget !== dropTarget) {
currentDropTarget.classList.remove(DROP_TARGET_ACTIVE_CLASS);
}
if (dropTarget) {
dropTarget.classList.add(DROP_TARGET_ACTIVE_CLASS);
currentDropTarget = dropTarget;
} else {
currentDropTarget = null;
}
}
// Export the handlers for use by Go via index.ts
export { handleDragEnter, handleDragLeave, handleDragOver };
/**
* A record describing the position of a window.
*/
@ -536,33 +627,34 @@ class Window {
}
/**
* Handles file drops originating from platform-specific code (e.g., macOS native drag-and-drop).
* Handles file drops originating from platform-specific code (e.g., macOS/Linux native drag-and-drop).
* Gathers information about the drop target element and sends it back to the Go backend.
*
* @param filenames - An array of file paths (strings) that were dropped.
* @param x - The x-coordinate of the drop event.
* @param y - The y-coordinate of the drop event.
* @param x - The x-coordinate of the drop event (CSS pixels).
* @param y - The y-coordinate of the drop event (CSS pixels).
*/
HandlePlatformFileDrop(filenames: string[], x: number, y: number): void {
// Check if file drops are enabled for this window
if ((window as any)._wails?.flags?.enableFileDrop === false) {
return; // File drops disabled, ignore the drop
}
const element = document.elementFromPoint(x, y);
const dropTarget = getDropTargetElement(element);
// NEW: Check if the drop target is a valid dropzone
const dropzoneTarget = getDropzoneElement(element);
if (!dropzoneTarget) {
console.log(`Wails Runtime: Drop on element (or no element) at ${x},${y} which is not a designated dropzone. Ignoring. Element:`, element);
// No need to call backend if not a valid dropzone target
if (!dropTarget) {
// Drop was not on a designated drop target - ignore
return;
}
console.log(`Wails Runtime: Drop on designated dropzone. Element at (${x}, ${y}):`, element, 'Effective dropzone:', dropzoneTarget);
const elementDetails = {
id: dropzoneTarget.id,
classList: Array.from(dropzoneTarget.classList),
id: dropTarget.id,
classList: Array.from(dropTarget.classList),
attributes: {} as { [key: string]: string },
};
for (let i = 0; i < dropzoneTarget.attributes.length; i++) {
const attr = dropzoneTarget.attributes[i];
for (let i = 0; i < dropTarget.attributes.length; i++) {
const attr = dropTarget.attributes[i];
elementDetails.attributes[attr.name] = attr.value;
}
@ -573,7 +665,10 @@ class Window {
elementDetails,
};
this[callerSym](WindowDropZoneDropped, payload);
this[callerSym](FilesDropped, payload);
// Clean up native drag state after drop
cleanupNativeDrag();
}
/* Triggers Windows 11 Snap Assist feature (Windows only).
@ -596,81 +691,146 @@ class Window {
*/
const thisWindow = new Window('');
// NEW: Global Drag Event Listeners
function setupGlobalDropzoneListeners() {
/**
* Sets up global drag and drop event listeners for file drops.
* Handles visual feedback (hover state) and file drop processing.
*/
function setupDropTargetListeners() {
const docElement = document.documentElement;
let dragEnterCounter = 0; // To handle dragenter/dragleave on child elements
let dragEnterCounter = 0;
docElement.addEventListener('dragenter', (event) => {
event.preventDefault();
if (event.dataTransfer && event.dataTransfer.types.includes('Files')) {
dragEnterCounter++;
const targetElement = document.elementFromPoint(event.clientX, event.clientY);
const dropzone = getDropzoneElement(targetElement);
if (!event.dataTransfer?.types.includes('Files')) {
return; // Only handle file drags, let other drags pass through
}
event.preventDefault(); // Always prevent default to stop browser navigation
// On Windows, check if file drops are enabled for this window
if ((window as any)._wails?.flags?.enableFileDrop === false) {
event.dataTransfer.dropEffect = 'none'; // Show "no drop" cursor
return; // File drops disabled, don't show hover effects
}
dragEnterCounter++;
const targetElement = document.elementFromPoint(event.clientX, event.clientY);
const dropTarget = getDropTargetElement(targetElement);
// Clear previous hover regardless, then apply new if valid
if (currentHoveredDropzone && currentHoveredDropzone !== dropzone) {
currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS);
}
// Update hover state
if (currentDropTarget && currentDropTarget !== dropTarget) {
currentDropTarget.classList.remove(DROP_TARGET_ACTIVE_CLASS);
}
if (dropzone) {
dropzone.classList.add(DROPZONE_HOVER_CLASS);
event.dataTransfer.dropEffect = 'copy';
currentHoveredDropzone = dropzone;
} else {
event.dataTransfer.dropEffect = 'none';
currentHoveredDropzone = null; // Ensure it's cleared if no dropzone found
}
if (dropTarget) {
dropTarget.classList.add(DROP_TARGET_ACTIVE_CLASS);
event.dataTransfer.dropEffect = 'copy';
currentDropTarget = dropTarget;
} else {
event.dataTransfer.dropEffect = 'none';
currentDropTarget = null;
}
}, false);
docElement.addEventListener('dragover', (event) => {
event.preventDefault(); // Necessary to allow drop
if (event.dataTransfer && event.dataTransfer.types.includes('Files')) {
// No need to query elementFromPoint again if already handled by dragenter correctly
// Just ensure dropEffect is continuously set based on currentHoveredDropzone
if (currentHoveredDropzone) {
// Re-apply class just in case it was removed by some other JS
if(!currentHoveredDropzone.classList.contains(DROPZONE_HOVER_CLASS)) {
currentHoveredDropzone.classList.add(DROPZONE_HOVER_CLASS);
}
event.dataTransfer.dropEffect = 'copy';
} else {
event.dataTransfer.dropEffect = 'none';
if (!event.dataTransfer?.types.includes('Files')) {
return; // Only handle file drags
}
event.preventDefault(); // Always prevent default to stop browser navigation
// On Windows, check if file drops are enabled for this window
if ((window as any)._wails?.flags?.enableFileDrop === false) {
event.dataTransfer.dropEffect = 'none'; // Show "no drop" cursor
return; // File drops disabled, don't show hover effects
}
// Update drop target as cursor moves
const targetElement = document.elementFromPoint(event.clientX, event.clientY);
const dropTarget = getDropTargetElement(targetElement);
if (currentDropTarget && currentDropTarget !== dropTarget) {
currentDropTarget.classList.remove(DROP_TARGET_ACTIVE_CLASS);
}
if (dropTarget) {
if (!dropTarget.classList.contains(DROP_TARGET_ACTIVE_CLASS)) {
dropTarget.classList.add(DROP_TARGET_ACTIVE_CLASS);
}
event.dataTransfer.dropEffect = 'copy';
currentDropTarget = dropTarget;
} else {
event.dataTransfer.dropEffect = 'none';
currentDropTarget = null;
}
}, false);
docElement.addEventListener('dragleave', (event) => {
event.preventDefault();
if (event.dataTransfer && event.dataTransfer.types.includes('Files')) {
dragEnterCounter--;
// Only remove hover if drag truly left the window or the last dropzone
if (dragEnterCounter === 0 || event.relatedTarget === null || (currentHoveredDropzone && !currentHoveredDropzone.contains(event.relatedTarget as Node))) {
if (currentHoveredDropzone) {
currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS);
currentHoveredDropzone = null;
}
dragEnterCounter = 0; // Reset counter if it went negative or left window
if (!event.dataTransfer?.types.includes('Files')) {
return;
}
event.preventDefault(); // Always prevent default to stop browser navigation
// On Windows, check if file drops are enabled for this window
if ((window as any)._wails?.flags?.enableFileDrop === false) {
return;
}
// On Linux/WebKitGTK and macOS, dragleave fires immediately with relatedTarget=null when native
// drag handling is involved. Ignore these spurious events - we'll clean up on drop instead.
if (event.relatedTarget === null) {
return;
}
dragEnterCounter--;
if (dragEnterCounter === 0 ||
(currentDropTarget && !currentDropTarget.contains(event.relatedTarget as Node))) {
if (currentDropTarget) {
currentDropTarget.classList.remove(DROP_TARGET_ACTIVE_CLASS);
currentDropTarget = null;
}
dragEnterCounter = 0;
}
}, false);
docElement.addEventListener('drop', (event) => {
event.preventDefault(); // Prevent default browser file handling
dragEnterCounter = 0; // Reset counter
if (currentHoveredDropzone) {
currentHoveredDropzone.classList.remove(DROPZONE_HOVER_CLASS);
currentHoveredDropzone = null;
if (!event.dataTransfer?.types.includes('Files')) {
return; // Only handle file drops
}
event.preventDefault(); // Always prevent default to stop browser navigation
// On Windows, check if file drops are enabled for this window
if ((window as any)._wails?.flags?.enableFileDrop === false) {
return;
}
dragEnterCounter = 0;
if (currentDropTarget) {
currentDropTarget.classList.remove(DROP_TARGET_ACTIVE_CLASS);
currentDropTarget = null;
}
// On Windows, handle file drops via JavaScript
// On macOS/Linux, native code will call HandlePlatformFileDrop
if (canResolveFilePaths()) {
const files: File[] = [];
if (event.dataTransfer.items) {
for (const item of event.dataTransfer.items) {
if (item.kind === 'file') {
const file = item.getAsFile();
if (file) files.push(file);
}
}
} else if (event.dataTransfer.files) {
for (const file of event.dataTransfer.files) {
files.push(file);
}
}
if (files.length > 0) {
resolveFilePaths(event.clientX, event.clientY, files);
}
}
// The actual drop processing is initiated by the native side calling HandlePlatformFileDrop
// HandlePlatformFileDrop will then check if the drop was on a valid zone.
}, false);
}
// Initialize listeners when the script loads
if (typeof window !== "undefined" && typeof document !== "undefined") {
setupGlobalDropzoneListeners();
setupDropTargetListeners();
}
export default thisWindow;

View file

@ -6,7 +6,7 @@ import (
json "github.com/goccy/go-json"
)
var runtimeInit = `window._wails=window._wails||{};window.wails=window.wails||{};`
var runtimeInit = `window._wails=window._wails||{};window._wails.flags=window._wails.flags||{};window.wails=window.wails||{};`
func Core(flags map[string]any) string {
flagsStr := ""

View file

@ -239,9 +239,9 @@ type OriginInfo struct {
var windowMessageBuffer = make(chan *windowMessage, 5)
// DropZoneDetails contains information about the HTML element
// at the location of a file drop.
type DropZoneDetails struct {
// DropTargetDetails contains information about the HTML element
// where files were dropped (the element with data-file-drop-target attribute).
type DropTargetDetails struct {
X int `json:"x"`
Y int `json:"y"`
ElementID string `json:"id"`
@ -250,20 +250,20 @@ type DropZoneDetails struct {
}
type dragAndDropMessage struct {
windowId uint
filenames []string
X int
Y int
DropZone *DropZoneDetails
windowId uint
filenames []string
X int
Y int
DropTarget *DropTargetDetails
}
var windowDragAndDropBuffer = make(chan *dragAndDropMessage, 5)
func addDragAndDropMessage(windowId uint, filenames []string, dropZone *DropZoneDetails) {
func addDragAndDropMessage(windowId uint, filenames []string, dropTarget *DropTargetDetails) {
windowDragAndDropBuffer <- &dragAndDropMessage{
windowId: windowId,
filenames: filenames,
DropZone: dropZone,
windowId: windowId,
filenames: filenames,
DropTarget: dropTarget,
}
}
@ -619,11 +619,6 @@ func (a *App) Run() error {
go func() {
for {
dragAndDropMessage := <-windowDragAndDropBuffer
a.Logger.Debug(
"[DragDropDebug] App.Run: Received message from windowDragAndDropBuffer",
"message",
fmt.Sprintf("%+v", dragAndDropMessage),
)
go a.handleDragAndDropMessage(dragAndDropMessage)
}
}()
@ -715,13 +710,7 @@ func (a *App) shutdownServices() {
}
func (a *App) handleDragAndDropMessage(event *dragAndDropMessage) {
a.Logger.Debug(
"[DragDropDebug] App.handleDragAndDropMessage: Called with event",
"event",
fmt.Sprintf("%+v", event),
)
defer handlePanic()
// Get window from window map
a.windowsLock.Lock()
window, ok := a.windows[event.windowId]
a.windowsLock.Unlock()
@ -729,13 +718,7 @@ func (a *App) handleDragAndDropMessage(event *dragAndDropMessage) {
a.warning("WebviewWindow #%d not found", event.windowId)
return
}
// Get callback from window
a.Logger.Debug(
"[DragDropDebug] App.handleDragAndDropMessage: Calling window.HandleDragAndDropMessage",
"windowID",
event.windowId,
)
window.HandleDragAndDropMessage(event.filenames, event.DropZone)
window.handleDragAndDropMessage(event.filenames, event.DropTarget)
}
func (a *App) handleWindowMessage(event *windowMessage) {

View file

@ -196,6 +196,8 @@ static void startSingleInstanceListener(const char *uniqueID) {
*/
import "C"
import (
"sync"
"time"
"unsafe"
json "github.com/goccy/go-json"
@ -414,6 +416,254 @@ func processDragItems(windowID C.uint, arr **C.char, length C.int, x C.int, y C.
targetWindow.InitiateFrontendDropProcessing(filenames, int(x), int(y))
}
//export macosOnDragEnter
func macosOnDragEnter(windowID C.uint) {
window, ok := globalApplication.Window.GetByID(uint(windowID))
if !ok || window == nil {
return
}
// Call JavaScript to show drag entered state
window.ExecJS("window._wails.handleDragEnter();")
}
//export macosOnDragExit
func macosOnDragExit(windowID C.uint) {
window, ok := globalApplication.Window.GetByID(uint(windowID))
if !ok || window == nil {
return
}
// Call JavaScript to clean up drag state
window.ExecJS("window._wails.handleDragLeave();")
}
var (
// Pre-allocated buffer for drag JS calls to avoid allocations
dragOverJSBuffer = make([]byte, 128) // Increased for safety
dragOverJSMutex sync.Mutex // Protects dragOverJSBuffer
dragOverJSPrefix = []byte("window._wails.handleDragOver(")
// Cache window references to avoid repeated lookups
windowImplCache sync.Map // windowID -> *macosWebviewWindow
// Per-window drag throttle state
dragThrottle sync.Map // windowID -> *dragThrottleState
)
type dragThrottleState struct {
mu sync.Mutex // Protects all fields below
lastX, lastY int
timer *time.Timer
pendingX int
pendingY int
hasPending bool
}
// clearWindowDragCache removes cached references for a window
func clearWindowDragCache(windowID uint) {
windowImplCache.Delete(windowID)
// Cancel any pending timer
if throttleVal, ok := dragThrottle.Load(windowID); ok {
if throttle, ok := throttleVal.(*dragThrottleState); ok {
throttle.mu.Lock()
if throttle.timer != nil {
throttle.timer.Stop()
}
throttle.mu.Unlock()
}
}
dragThrottle.Delete(windowID)
}
// writeInt writes an integer to a byte slice and returns the number of bytes written
func writeInt(buf []byte, n int) int {
if n < 0 {
if len(buf) == 0 {
return 0
}
buf[0] = '-'
return 1 + writeInt(buf[1:], -n)
}
if n == 0 {
if len(buf) == 0 {
return 0
}
buf[0] = '0'
return 1
}
// Count digits
tmp := n
digits := 0
for tmp > 0 {
digits++
tmp /= 10
}
// Bounds check
if digits > len(buf) {
return 0
}
// Write digits in reverse
for i := digits - 1; i >= 0; i-- {
buf[i] = byte('0' + n%10)
n /= 10
}
return digits
}
//export macosOnDragOver
func macosOnDragOver(windowID C.uint, x C.int, y C.int) {
winID := uint(windowID)
intX, intY := int(x), int(y)
// Get or create throttle state
throttleKey := winID
throttleVal, _ := dragThrottle.LoadOrStore(throttleKey, &dragThrottleState{
lastX: intX,
lastY: intY,
})
throttle := throttleVal.(*dragThrottleState)
throttle.mu.Lock()
// Update pending position
throttle.pendingX = intX
throttle.pendingY = intY
throttle.hasPending = true
// If timer is already running, just update the pending position
if throttle.timer != nil {
throttle.mu.Unlock()
return
}
// Apply 5-pixel threshold for immediate update
dx := intX - throttle.lastX
dy := intY - throttle.lastY
if dx < 0 {
dx = -dx
}
if dy < 0 {
dy = -dy
}
// Check if we should send an immediate update
shouldSendNow := dx >= 5 || dy >= 5
if shouldSendNow {
// Update last position
throttle.lastX = intX
throttle.lastY = intY
throttle.hasPending = false
// Send this update immediately (unlock before JS call to avoid deadlock)
throttle.mu.Unlock()
sendDragUpdate(winID, intX, intY)
throttle.mu.Lock()
}
// Start 50ms timer for next update (whether we sent now or not)
throttle.timer = time.AfterFunc(50*time.Millisecond, func() {
// Execute on main thread to ensure UI updates
InvokeSync(func() {
throttle.mu.Lock()
// Clear timer reference
throttle.timer = nil
// Send pending update if any
if throttle.hasPending {
pendingX, pendingY := throttle.pendingX, throttle.pendingY
throttle.lastX = pendingX
throttle.lastY = pendingY
throttle.hasPending = false
throttle.mu.Unlock()
sendDragUpdate(winID, pendingX, pendingY)
} else {
throttle.mu.Unlock()
}
})
})
throttle.mu.Unlock()
}
// sendDragUpdate sends the actual drag update to JavaScript
func sendDragUpdate(winID uint, x, y int) {
// Try cached implementation first
var darwinImpl *macosWebviewWindow
var needsExecJS bool
if cached, found := windowImplCache.Load(winID); found {
darwinImpl = cached.(*macosWebviewWindow)
if darwinImpl != nil && darwinImpl.nsWindow != nil {
needsExecJS = true
} else {
// Invalid cache entry, remove it
windowImplCache.Delete(winID)
}
}
if !needsExecJS {
// Fallback to full lookup
window, ok := globalApplication.Window.GetByID(winID)
if !ok || window == nil {
return
}
// Type assert to WebviewWindow
webviewWindow, ok := window.(*WebviewWindow)
if !ok || webviewWindow == nil {
return
}
// Get implementation
darwinImpl, ok = webviewWindow.impl.(*macosWebviewWindow)
if !ok {
return
}
// Cache for next time
windowImplCache.Store(winID, darwinImpl)
needsExecJS = true
}
if !needsExecJS || darwinImpl == nil {
return
}
// Protect shared buffer access
dragOverJSMutex.Lock()
// Build JS string with zero allocations
// Format: "window._wails.handleDragOver(X,Y)"
// Max length with int32 coords: 30 + 11 + 1 + 11 + 1 + 1 = 55 bytes
n := copy(dragOverJSBuffer[:], dragOverJSPrefix)
n += writeInt(dragOverJSBuffer[n:], x)
if n < len(dragOverJSBuffer) {
dragOverJSBuffer[n] = ','
n++
}
n += writeInt(dragOverJSBuffer[n:], y)
if n < len(dragOverJSBuffer) {
dragOverJSBuffer[n] = ')'
n++
}
if n < len(dragOverJSBuffer) {
dragOverJSBuffer[n] = 0 // null terminate for C
} else {
// Buffer overflow - this should not happen with 128 byte buffer
dragOverJSMutex.Unlock()
return
}
// Call JavaScript with zero allocations
darwinImpl.execJSDragOver(dragOverJSBuffer[:n+1]) // Include null terminator
dragOverJSMutex.Unlock()
}
//export processMenuItemClick
func processMenuItemClick(menuID C.uint) {
menuItemClicked <- uint(menuID)

View file

@ -3,8 +3,8 @@ package application
var blankWindowEventContext = &WindowEventContext{}
const (
droppedFiles = "droppedFiles"
dropZoneDetailsKey = "dropZoneDetails"
droppedFiles = "droppedFiles"
dropTargetDetailsKey = "dropTargetDetails"
)
type WindowEventContext struct {
@ -42,33 +42,31 @@ func (c WindowEventContext) setCoordinates(x, y int) {
c.data["y"] = y
}
func (c WindowEventContext) setDropZoneDetails(details *DropZoneDetails) {
func (c WindowEventContext) setDropTargetDetails(details *DropTargetDetails) {
if c.data == nil {
c.data = make(map[string]any)
}
if details == nil {
c.data[dropZoneDetailsKey] = nil
c.data[dropTargetDetailsKey] = nil
return
}
c.data[dropZoneDetailsKey] = details
c.data[dropTargetDetailsKey] = details
}
// DropZoneDetails retrieves the detailed drop zone information, if available.
func (c WindowEventContext) DropZoneDetails() *DropZoneDetails {
// DropTargetDetails retrieves information about the drop target element.
func (c WindowEventContext) DropTargetDetails() *DropTargetDetails {
if c.data == nil {
c.data = make(map[string]any)
}
details, ok := c.data[dropZoneDetailsKey]
details, ok := c.data[dropTargetDetailsKey]
if !ok {
return nil
}
// Explicitly type assert, handle if it's nil (though setDropZoneDetails should handle it)
if details == nil {
return nil
}
result, ok := details.(*DropZoneDetails)
result, ok := details.(*DropTargetDetails)
if !ok {
// This case indicates a programming error if data was set incorrectly
return nil
}
return result

View file

@ -23,11 +23,9 @@ import (
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#ifdef G_APPLICATION_DEFAULT_FLAGS
#define APPLICATION_DEFAULT_FLAGS G_APPLICATION_DEFAULT_FLAGS
#else
#define APPLICATION_DEFAULT_FLAGS G_APPLICATION_FLAGS_NONE
#endif
// Use NON_UNIQUE to allow multiple instances of the application to run.
// This matches the behavior of gtk_init/gtk_main used in v2.
#define APPLICATION_DEFAULT_FLAGS G_APPLICATION_NON_UNIQUE
typedef struct CallbackID
{
@ -87,6 +85,9 @@ void handleClick(void*);
extern gboolean onButtonEvent(GtkWidget *widget, GdkEventButton *event, uintptr_t user_data);
extern gboolean onMenuButtonEvent(GtkWidget *widget, GdkEventButton *event, uintptr_t user_data);
extern void onUriList(char **extracted, gint x, gint y, gpointer data);
extern void onDragEnter(gpointer data);
extern void onDragLeave(gpointer data);
extern void onDragOver(gint x, gint y, gpointer data);
extern gboolean onKeyPressEvent (GtkWidget *widget, GdkEventKey *event, uintptr_t user_data);
extern void onProcessRequest(WebKitURISchemeRequest *request, uintptr_t user_data);
extern void sendMessageToBackend(WebKitUserContentManager *contentManager, WebKitJavascriptResult *result, void *data);
@ -224,34 +225,204 @@ static int GetNumScreens(){
return 0;
}
static void on_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
// Handle file drops from the OS - called when drag data is received
static void on_drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
GtkSelectionData *selection_data, guint target_type, guint time,
gpointer data)
{
gint length = gtk_selection_data_get_length(selection_data);
if (length < 0)
{
g_print("DnD failed!\n");
gtk_drag_finish(context, FALSE, FALSE, time);
// Only process target_type 2 which is our text/uri-list
// Other target types are from internal WebKit drags
if (target_type != 2) {
return; // Don't interfere with internal drags
}
gchar *uri_data = (gchar *)gtk_selection_data_get_data(selection_data);
gchar **uri_list = g_uri_list_extract_uris(uri_data);
// Check if we have valid data
if (selection_data == NULL || gtk_selection_data_get_length(selection_data) <= 0) {
gtk_drag_finish(context, FALSE, FALSE, time);
return;
}
onUriList(uri_list, x, y, data);
const gchar *uri_data = (const gchar *)gtk_selection_data_get_data(selection_data);
gchar **filenames = g_uri_list_extract_uris(uri_data);
if (filenames == NULL || filenames[0] == NULL) {
if (filenames) g_strfreev(filenames);
gtk_drag_finish(context, FALSE, FALSE, time);
return;
}
g_strfreev(uri_list);
gtk_drag_finish(context, TRUE, TRUE, time);
// Build file array for Go
GPtrArray *file_array = g_ptr_array_new();
int iter = 0;
while (filenames[iter] != NULL) {
char *filename = g_filename_from_uri(filenames[iter], NULL, NULL);
if (filename != NULL) {
g_ptr_array_add(file_array, filename);
}
iter++;
}
g_strfreev(filenames);
if (file_array->len > 0) {
// Get stored drop coordinates and data pointer
gint drop_x = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "drop-x"));
gint drop_y = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "drop-y"));
gpointer drop_data = g_object_get_data(G_OBJECT(widget), "drop-data");
// Add NULL terminator and call Go
g_ptr_array_add(file_array, NULL);
onUriList((gchar **)file_array->pdata, drop_x, drop_y, drop_data);
}
// Cleanup
for (guint i = 0; i < file_array->len; i++) {
gpointer item = g_ptr_array_index(file_array, i);
if (item) g_free(item);
}
g_ptr_array_free(file_array, TRUE);
// Finish the drag successfully to prevent WebKit from opening the file
gtk_drag_finish(context, TRUE, FALSE, time);
}
// drag and drop tutorial: https://wiki.gnome.org/Newcomers/OldDragNDropTutorial
// Track if we've notified about drag entering
static gboolean drag_entered = FALSE;
// Track if a drag started from within the webview (internal HTML5 drag)
static gboolean internal_drag_active = FALSE;
// Called when a drag starts FROM this widget (internal drag)
static void on_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
{
internal_drag_active = TRUE;
}
// Called when a drag that started from this widget ends
static void on_drag_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
{
internal_drag_active = FALSE;
}
// Check if a drag context contains file URIs (external drop)
// Returns TRUE only for external file manager drops, FALSE for internal HTML5 drags
static gboolean is_file_drag(GdkDragContext *context)
{
GList *targets = gdk_drag_context_list_targets(context);
// Internal HTML5 drags have WebKit-specific targets, external file drops have text/uri-list
for (GList *l = targets; l != NULL; l = l->next) {
GdkAtom atom = GDK_POINTER_TO_ATOM(l->data);
gchar *name = gdk_atom_name(atom);
if (name) {
gboolean is_uri = g_strcmp0(name, "text/uri-list") == 0;
g_free(name);
if (is_uri) {
return TRUE;
}
}
}
return FALSE;
}
// Handle the actual drop - called when user releases mouse button
static gboolean on_drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
guint time, gpointer data)
{
// Only handle external file drops, let WebKit handle internal HTML5 drags
if (!is_file_drag(context)) {
return FALSE;
}
// Reset drag entered state
drag_entered = FALSE;
// Store coordinates for use in drag-data-received
g_object_set_data(G_OBJECT(widget), "drop-x", GINT_TO_POINTER(x));
g_object_set_data(G_OBJECT(widget), "drop-y", GINT_TO_POINTER(y));
g_object_set_data(G_OBJECT(widget), "drop-data", data);
// Request the file data - this triggers drag-data-received
GdkAtom target = gdk_atom_intern("text/uri-list", FALSE);
gtk_drag_get_data(widget, context, target, time);
return TRUE;
}
// Handle drag-motion for hover effects on external file drags
static gboolean on_drag_motion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer data)
{
// Don't handle internal HTML5 drags
if (internal_drag_active || !is_file_drag(context)) {
return FALSE;
}
gdk_drag_status(context, GDK_ACTION_COPY, time);
// Notify JS once when drag enters
if (!drag_entered) {
drag_entered = TRUE;
onDragEnter(data);
}
// Send position to JS for hover effects (Go side throttles this)
onDragOver(x, y, data);
return TRUE;
}
// Handle drag-leave - drag exited the window
static void on_drag_leave(GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
{
// Don't handle internal HTML5 drags
if (internal_drag_active || !is_file_drag(context)) {
return;
}
if (drag_entered) {
drag_entered = FALSE;
onDragLeave(data);
}
}
// Set up drag and drop handlers for external file drops with hover effects
static void enableDND(GtkWidget *widget, gpointer data)
{
GtkTargetEntry *target = gtk_target_entry_new("text/uri-list", 0, 0);
gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, target, 1, GDK_ACTION_COPY);
// Core handlers for file drop
g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(on_drag_data_received), data);
g_signal_connect(G_OBJECT(widget), "drag-drop", G_CALLBACK(on_drag_drop), data);
signal_connect(widget, "drag-data-received", on_data_received, data);
// Hover effect handlers - return FALSE for internal drags to let WebKit handle them
g_signal_connect(G_OBJECT(widget), "drag-motion", G_CALLBACK(on_drag_motion), data);
g_signal_connect(G_OBJECT(widget), "drag-leave", G_CALLBACK(on_drag_leave), data);
}
// Block external file drops - consume the events to prevent WebKit from navigating to files
// Returns TRUE for file drags to consume them, FALSE for internal HTML5 drags to let WebKit handle
static gboolean on_drag_drop_blocked(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
guint time, gpointer data)
{
if (!is_file_drag(context)) {
return FALSE; // Let WebKit handle internal HTML5 drags
}
// Block external file drops by finishing with failure
gtk_drag_finish(context, FALSE, FALSE, time);
return TRUE;
}
static gboolean on_drag_motion_blocked(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer data)
{
if (internal_drag_active || !is_file_drag(context)) {
return FALSE; // Let WebKit handle internal HTML5 drags
}
// Show "no drop" cursor for external file drags
gdk_drag_status(context, 0, time);
return TRUE;
}
// Set up handlers that block external file drops while allowing internal HTML5 drag-and-drop
static void disableDND(GtkWidget *widget, gpointer data)
{
g_signal_connect(G_OBJECT(widget), "drag-drop", G_CALLBACK(on_drag_drop_blocked), data);
g_signal_connect(G_OBJECT(widget), "drag-motion", G_CALLBACK(on_drag_motion_blocked), data);
}
*/
import "C"
@ -848,10 +1019,15 @@ func (w *linuxWebviewWindow) close() {
}
func (w *linuxWebviewWindow) enableDND() {
C.gtk_drag_dest_unset((*C.GtkWidget)(w.webview))
// Pass window ID as pointer value (not pointer to ID) - same pattern as other signal handlers
winID := unsafe.Pointer(uintptr(w.parent.id))
C.enableDND((*C.GtkWidget)(w.webview), C.gpointer(winID))
}
windowId := C.uint(w.parent.id)
C.enableDND((*C.GtkWidget)(w.vbox), C.gpointer(&windowId))
func (w *linuxWebviewWindow) disableDND() {
// Block external file drops while allowing internal HTML5 drag-and-drop
winID := unsafe.Pointer(uintptr(w.parent.id))
C.disableDND((*C.GtkWidget)(w.webview), C.gpointer(winID))
}
func (w *linuxWebviewWindow) execJS(js string) {
@ -869,6 +1045,61 @@ func (w *linuxWebviewWindow) execJS(js string) {
})
}
// Preallocated buffer for drag-over JS calls to avoid allocations
// "window._wails.handleDragOver(XXXXX,YYYYY)" is max ~45 chars
var dragOverJSBuffer = C.CString(strings.Repeat(" ", 64))
var emptyWorldName = C.CString("")
// execJSDragOver executes JS for drag-over events with zero Go allocations.
// It directly writes to a preallocated C buffer. Must be called from main thread.
func (w *linuxWebviewWindow) execJSDragOver(x, y int) {
// Format: "window._wails.handleDragOver(X,Y)"
// Write directly to C buffer
buf := (*[64]byte)(unsafe.Pointer(dragOverJSBuffer))
n := copy(buf[:], "window._wails.handleDragOver(")
n += writeInt(buf[n:], x)
buf[n] = ','
n++
n += writeInt(buf[n:], y)
buf[n] = ')'
n++
buf[n] = 0 // null terminate
C.webkit_web_view_evaluate_javascript(w.webKitWebView(),
dragOverJSBuffer,
C.long(n),
nil,
emptyWorldName,
nil,
nil,
nil)
}
// writeInt writes an integer to a byte slice and returns the number of bytes written
func writeInt(buf []byte, n int) int {
if n < 0 {
buf[0] = '-'
return 1 + writeInt(buf[1:], -n)
}
if n == 0 {
buf[0] = '0'
return 1
}
// Count digits
tmp := n
digits := 0
for tmp > 0 {
digits++
tmp /= 10
}
// Write digits in reverse
for i := digits - 1; i >= 0; i-- {
buf[i] = byte('0' + n%10)
n /= 10
}
return digits
}
func getMousePosition() (int, int, *Screen) {
var x, y C.gint
var screen *C.GdkScreen
@ -1568,6 +1799,45 @@ func onMenuButtonEvent(_ *C.GtkWidget, event *C.GdkEventButton, data C.uintptr_t
return C.gboolean(0)
}
//export onDragEnter
func onDragEnter(data unsafe.Pointer) {
windowId := uint(uintptr(data))
targetWindow, ok := globalApplication.Window.GetByID(windowId)
if !ok || targetWindow == nil {
return
}
// HandleDragEnter is Linux-specific (GTK intercepts drag events)
if w, ok := targetWindow.(*WebviewWindow); ok {
w.HandleDragEnter()
}
}
//export onDragLeave
func onDragLeave(data unsafe.Pointer) {
windowId := uint(uintptr(data))
targetWindow, ok := globalApplication.Window.GetByID(windowId)
if !ok || targetWindow == nil {
return
}
// HandleDragLeave is Linux-specific (GTK intercepts drag events)
if w, ok := targetWindow.(*WebviewWindow); ok {
w.HandleDragLeave()
}
}
//export onDragOver
func onDragOver(x C.gint, y C.gint, data unsafe.Pointer) {
windowId := uint(uintptr(data))
targetWindow, ok := globalApplication.Window.GetByID(windowId)
if !ok || targetWindow == nil {
return
}
// HandleDragOver is Linux-specific (GTK intercepts drag events)
if w, ok := targetWindow.(*WebviewWindow); ok {
w.HandleDragOver(int(x), int(y))
}
}
//export onUriList
func onUriList(extracted **C.char, x C.gint, y C.gint, data unsafe.Pointer) {
// Credit: https://groups.google.com/g/golang-nuts/c/bI17Bpck8K4/m/DVDa7EMtDAAJ
@ -1578,12 +1848,17 @@ func onUriList(extracted **C.char, x C.gint, y C.gint, data unsafe.Pointer) {
extracted = (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(extracted)) + offset))
}
windowDragAndDropBuffer <- &dragAndDropMessage{
windowId: uint(*((*C.uint)(data))),
filenames: filenames,
X: int(x),
Y: int(y),
// Window ID is stored as the pointer value itself (not pointing to memory)
// Same pattern as other signal handlers in this file
windowId := uint(uintptr(data))
targetWindow, ok := globalApplication.Window.GetByID(windowId)
if !ok || targetWindow == nil {
globalApplication.error("onUriList could not find window with ID: %d", windowId)
return
}
// Send to frontend for drop target detection and filtering
targetWindow.InitiateFrontendDropProcessing(filenames, int(x), int(y))
}
var debounceTimer *time.Timer

View file

@ -393,12 +393,15 @@ func appName() string {
}
func appNew(name string) pointer {
GApplicationDefaultFlags := uint(0)
// Use NON_UNIQUE to allow multiple instances of the application to run
// This matches the behavior of gtk_init/gtk_main used in v2
// G_APPLICATION_NON_UNIQUE = (1 << 5) = 32
GApplicationNonUnique := uint(32)
// Name is already sanitized by sanitizeAppName() in application_linux.go
identifier := fmt.Sprintf("org.wails.%s", name)
return pointer(gtkApplicationNew(identifier, GApplicationDefaultFlags))
return pointer(gtkApplicationNew(identifier, GApplicationNonUnique))
}
func appRun(application pointer) error {

View file

@ -2,7 +2,6 @@ package application
import (
"fmt"
"log/slog"
"github.com/wailsapp/wails/v3/pkg/errs"
)
@ -58,7 +57,7 @@ const (
WindowZoomOut = 47
WindowZoomReset = 48
WindowSnapAssist = 49
WindowDropZoneDropped = 50
WindowFilesDropped = 50
WindowPrint = 51
)
@ -112,7 +111,7 @@ var windowMethodNames = map[int]string{
WindowZoomIn: "ZoomIn",
WindowZoomOut: "ZoomOut",
WindowZoomReset: "ZoomReset",
WindowDropZoneDropped: "DropZoneDropped",
WindowFilesDropped: "FilesDropped",
WindowSnapAssist: "SnapAssist",
WindowPrint: "Print",
}
@ -354,18 +353,11 @@ func (m *MessageProcessor) processWindowMethod(
case WindowZoomReset:
window.ZoomReset()
return unit, nil
case WindowDropZoneDropped:
m.Info(
"[DragDropDebug] processWindowMethod: Entered WindowDropZoneDropped case",
)
slog.Info("[DragDropDebug] Raw 'args' payload string:", "data", string(req.Args.rawData))
case WindowFilesDropped:
var payload fileDropPayload
err := req.Args.ToStruct(&payload)
if err != nil {
return nil, errs.WrapInvalidWindowCallErrorf(err, "Error decoding file drop payload from 'args' parameter")
return nil, errs.WrapInvalidWindowCallErrorf(err, "error decoding file drop payload")
}
m.Info(
"[DragDropDebug] processWindowMethod: Decoded payload from 'args'",
@ -373,30 +365,24 @@ func (m *MessageProcessor) processWindowMethod(
fmt.Sprintf("%+v", payload),
)
dropDetails := &DropZoneDetails{
dropTarget := &DropTargetDetails{
X: payload.X,
Y: payload.Y,
ElementID: payload.ElementDetails.ID,
ClassList: payload.ElementDetails.ClassList,
Attributes: payload.ElementDetails.Attributes, // Assumes DropZoneDetails struct is updated to include this field
Attributes: payload.ElementDetails.Attributes,
}
wvWindow, ok := window.(*WebviewWindow)
if !ok {
return nil, errs.NewInvalidWindowCallErrorf("Error: Target window is not a WebviewWindow for FilesDroppedWithContext")
return nil, errs.NewInvalidWindowCallErrorf("target window is not a WebviewWindow")
}
msg := &dragAndDropMessage{
windowId: wvWindow.id,
filenames: payload.Filenames,
DropZone: dropDetails,
windowId: wvWindow.id,
filenames: payload.Filenames,
DropTarget: dropTarget,
}
m.Info(
"[DragDropDebug] processApplicationMethod: Sending message to windowDragAndDropBuffer",
"message",
fmt.Sprintf("%+v", msg),
)
windowDragAndDropBuffer <- msg
return unit, nil
case WindowSnapAssist:

View file

@ -1180,7 +1180,7 @@ func (w *WebviewWindow) SetFrameless(frameless bool) Window {
}
func (w *WebviewWindow) DispatchWailsEvent(event *CustomEvent) {
msg := fmt.Sprintf("_wails.dispatchWailsEvent(%s);", event.ToJSON())
msg := fmt.Sprintf("window._wails.dispatchWailsEvent(%s);", event.ToJSON())
w.ExecJS(msg)
}
@ -1205,37 +1205,16 @@ func (w *WebviewWindow) Error(message string, args ...any) {
globalApplication.error("in window '%s': "+message, args...)
}
func (w *WebviewWindow) HandleDragAndDropMessage(filenames []string, dropZone *DropZoneDetails) {
globalApplication.debug(
"[DragDropDebug] HandleDragAndDropMessage called",
"files", filenames,
"dropZone", dropZone,
)
func (w *WebviewWindow) handleDragAndDropMessage(filenames []string, dropTarget *DropTargetDetails) {
thisEvent := NewWindowEvent()
globalApplication.debug(
"[DragDropDebug] HandleDragAndDropMessage: thisEvent created",
"ctx", thisEvent.ctx,
)
ctx := newWindowEventContext()
ctx.setDroppedFiles(filenames)
if dropZone != nil { // Check if dropZone details are available
ctx.setDropZoneDetails(dropZone)
if dropTarget != nil {
ctx.setDropTargetDetails(dropTarget)
}
thisEvent.ctx = ctx
globalApplication.debug(
"[DragDropDebug] HandleDragAndDropMessage: thisEvent.ctx assigned",
"thisEvent.ctx", thisEvent.ctx,
"ctx", ctx,
)
listeners := w.eventListeners[uint(events.Common.WindowDropZoneFilesDropped)]
globalApplication.debug(
"[DragDropDebug] HandleDragAndDropMessage: Found listeners for WindowDropZoneFilesDropped",
"count", len(listeners),
)
globalApplication.debug(
"[DragDropDebug] HandleDragAndDropMessage: Before calling listeners",
"thisEvent.ctx", thisEvent.ctx,
)
listeners := w.eventListeners[uint(events.Common.WindowFilesDropped)]
for _, listener := range listeners {
if listener == nil {
continue
@ -1454,11 +1433,6 @@ func (w *WebviewWindow) ToggleMenuBar() {
}
func (w *WebviewWindow) InitiateFrontendDropProcessing(filenames []string, x int, y int) {
globalApplication.debug(
"[DragDropDebug] InitiateFrontendDropProcessing called",
"x", x,
"y", y,
)
if w.impl == nil || w.isDestroyed() {
return
}
@ -1470,7 +1444,7 @@ func (w *WebviewWindow) InitiateFrontendDropProcessing(filenames []string, x int
}
jsCall := fmt.Sprintf(
"window._wails.handlePlatformFileDrop(%s, %d, %d);",
"window.wails.Window.HandlePlatformFileDrop(%s, %d, %d);",
string(filenamesJSON),
x,
y,
@ -1487,6 +1461,65 @@ func (w *WebviewWindow) InitiateFrontendDropProcessing(filenames []string, x int
})
}
// HandleDragEnter is called when drag enters the window (Linux only, since GTK intercepts drag events)
func (w *WebviewWindow) HandleDragEnter() {
if w.impl == nil || w.isDestroyed() || !w.runtimeLoaded {
return
}
// Reset drag hover state for new drag session
dragHover.lastSentX = 0
dragHover.lastSentY = 0
w.impl.execJS("window._wails.handleDragEnter();")
}
// Drag hover throttle state
var dragHover struct {
lastSentX int
lastSentY int
}
// HandleDragOver is called during drag-motion to update hover state in JS
// This is called from the GTK main thread, so we can call execJS directly
func (w *WebviewWindow) HandleDragOver(x int, y int) {
if w.impl == nil || w.isDestroyed() || !w.runtimeLoaded {
return
}
// Throttle: only send if moved at least 5 pixels
dx := x - dragHover.lastSentX
dy := y - dragHover.lastSentY
if dx < 0 {
dx = -dx
}
if dy < 0 {
dy = -dy
}
if dx < 5 && dy < 5 {
return
}
dragHover.lastSentX = x
dragHover.lastSentY = y
// Use platform-specific zero-alloc implementation if available
if impl, ok := w.impl.(interface{ execJSDragOver(x, y int) }); ok {
impl.execJSDragOver(x, y)
} else {
w.impl.execJS(fmt.Sprintf("window._wails.handleDragOver(%d,%d)", x, y))
}
}
// HandleDragLeave is called when drag leaves the window
func (w *WebviewWindow) HandleDragLeave() {
if w.impl == nil || w.isDestroyed() || !w.runtimeLoaded {
return
}
// Don't use InvokeSync - execJS already handles main thread dispatch internally
w.impl.execJS("window._wails.handleDragLeave();")
}
// SnapAssist triggers the Windows Snap Assist feature by simulating Win+Z key combination.
// On Windows, this opens the snap layout options. On Linux and macOS, this is a no-op.
func (w *WebviewWindow) SnapAssist() {

View file

@ -352,6 +352,12 @@ void windowExecJS(void* nsWindow, const char* js) {
free((void*)js);
}
// Execute JS without allocation - buffer is NOT freed
void windowExecJSNoAlloc(void* nsWindow, const char* js) {
WebviewWindow* window = (WebviewWindow*)nsWindow;
[window.webView evaluateJavaScript:[NSString stringWithUTF8String:js] completionHandler:nil];
}
// Make NSWindow backdrop translucent
void windowSetTranslucent(void* nsWindow) {
// Get window
@ -847,6 +853,7 @@ static void setContentProtection(void *nsWindow, bool enabled) {
*/
import "C"
import (
"fmt"
"sync"
"sync/atomic"
"unsafe"
@ -1094,6 +1101,17 @@ func (w *macosWebviewWindow) execJS(js string) {
})
}
// execJSDragOver executes JS for drag-over events with zero allocations
// Must be called from main thread
func (w *macosWebviewWindow) execJSDragOver(buffer []byte) {
if w.nsWindow == nil {
return
}
// Pass buffer directly to C without allocation
// Buffer must be null-terminated
C.windowExecJSNoAlloc(w.nsWindow, (*C.char)(unsafe.Pointer(&buffer[0])))
}
func (w *macosWebviewWindow) setURL(uri string) {
C.navigationLoadURL(w.nsWindow, C.CString(uri))
}
@ -1107,7 +1125,10 @@ func newWindowImpl(parent *WebviewWindow) *macosWebviewWindow {
parent: parent,
}
result.parent.RegisterHook(events.Mac.WebViewDidFinishNavigation, func(event *WindowEvent) {
result.execJS(runtime.Core(globalApplication.impl.GetFlags(globalApplication.options)))
// Inject runtime core
js := runtime.Core(globalApplication.impl.GetFlags(globalApplication.options))
js += fmt.Sprintf("window._wails.flags.enableFileDrop=%v;", result.parent.options.EnableFileDrop)
result.execJS(js)
})
return result
}
@ -1241,7 +1262,7 @@ func (w *macosWebviewWindow) run() {
C.int(options.Height),
C.bool(macOptions.EnableFraudulentWebsiteWarnings),
C.bool(options.Frameless),
C.bool(options.EnableDragAndDrop),
C.bool(options.EnableFileDrop),
w.getWebviewPreferences(),
)
w.setTitle(options.Title)
@ -1477,6 +1498,8 @@ func (w *macosWebviewWindow) setPhysicalBounds(physicalBounds Rect) {
func (w *macosWebviewWindow) destroy() {
w.parent.markAsDestroyed()
// Clear caches for this window
clearWindowDragCache(w.parent.id)
C.windowDestroy(w.nsWindow)
}

View file

@ -196,7 +196,6 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) {
[super setDelegate: delegate];
// If the delegate is our WebviewWindowDelegate (which handles NSDraggingDestination)
if ([delegate isKindOfClass:[WebviewWindowDelegate class]]) {
NSLog(@"WebviewWindow: setDelegate - Registering window for dragged types (NSFilenamesPboardType) because WebviewWindowDelegate is being set.");
[self registerForDraggedTypes:@[NSFilenamesPboardType]]; // 'self' is the WebviewWindow instance
}
}
@ -246,53 +245,40 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) {
@end
@implementation WebviewWindowDelegate
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
NSLog(@"WebviewWindowDelegate: draggingEntered called. WindowID: %u", self.windowId);
NSPasteboard *pasteboard = [sender draggingPasteboard];
if ([[pasteboard types] containsObject:NSFilenamesPboardType]) {
NSLog(@"WebviewWindowDelegate: draggingEntered - Found NSFilenamesPboardType. Firing EventWindowFileDraggingEntered.");
// We need to ensure processWindowEvent is available or adapt this part
// For now, let's assume it's available globally or via an import
if (hasListeners(EventWindowFileDraggingEntered)) {
processWindowEvent(self.windowId, EventWindowFileDraggingEntered);
}
return NSDragOperationCopy;
}
NSLog(@"WebviewWindowDelegate: draggingEntered - NSFilenamesPboardType NOT found.");
return NSDragOperationNone;
}
- (void)draggingExited:(id<NSDraggingInfo>)sender {
NSLog(@"WebviewWindowDelegate: draggingExited called. WindowID: %u", self.windowId);
if (hasListeners(EventWindowFileDraggingExited)) {
processWindowEvent(self.windowId, EventWindowFileDraggingExited);
}
}
- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)sender {
NSLog(@"WebviewWindowDelegate: prepareForDragOperation called. WindowID: %u", self.windowId);
return YES;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
NSLog(@"WebviewWindowDelegate: performDragOperation called. WindowID: %u", self.windowId);
NSPasteboard *pasteboard = [sender draggingPasteboard];
if (hasListeners(EventWindowFileDraggingPerformed)) {
processWindowEvent(self.windowId, EventWindowFileDraggingPerformed);
}
if ([[pasteboard types] containsObject:NSFilenamesPboardType]) {
NSLog(@"WebviewWindowDelegate: performDragOperation - Found NSFilenamesPboardType.");
NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
NSUInteger count = [files count];
NSLog(@"WebviewWindowDelegate: performDragOperation - File count: %lu", (unsigned long)count);
if (count == 0) {
NSLog(@"WebviewWindowDelegate: performDragOperation - No files found in pasteboard, though type was present.");
return NO;
}
char** cArray = (char**)malloc(count * sizeof(char*));
if (cArray == NULL) {
NSLog(@"WebviewWindowDelegate: performDragOperation - Failed to allocate memory for file array.");
return NO;
}
for (NSUInteger i = 0; i < count; i++) {
NSString* str = files[i];
NSLog(@"WebviewWindowDelegate: performDragOperation - File %lu: %@", (unsigned long)i, str);
cArray[i] = (char*)[str UTF8String];
}
// Get the WebviewWindow instance, which is the dragging destination
@ -303,14 +289,10 @@ typedef NS_ENUM(NSInteger, MacLiquidGlassStyle) {
CGFloat viewHeight = webView.frame.size.height;
int x = (int)dropPointInView.x;
int y = (int)(viewHeight - dropPointInView.y); // Flip Y for web coordinate system
NSLog(@"WebviewWindowDelegate: performDragOperation - Coords: x=%d, y=%d. ViewHeight: %f", x, y, viewHeight);
NSLog(@"WebviewWindowDelegate: performDragOperation - Calling processDragItems for windowId %u.", self.windowId);
processDragItems(self.windowId, cArray, (int)count, x, y); // self.windowId is from the delegate
free(cArray);
NSLog(@"WebviewWindowDelegate: performDragOperation - Returned from processDragItems.");
return NO;
}
NSLog(@"WebviewWindowDelegate: performDragOperation - NSFilenamesPboardType NOT found. Returning NO.");
return NO;
}
// Original WebviewWindowDelegate methods continue here...

View file

@ -7,6 +7,9 @@
#import "../events/events_darwin.h"
extern void processDragItems(unsigned int windowId, char** arr, int length, int x, int y);
extern void macosOnDragEnter(unsigned int windowId);
extern void macosOnDragExit(unsigned int windowId);
extern void macosOnDragOver(unsigned int windowId, int x, int y);
@implementation WebviewDrag
@ -14,7 +17,6 @@ extern void processDragItems(unsigned int windowId, char** arr, int length, int
- (instancetype)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
NSLog(@"WebviewDrag: initWithFrame - Registering for dragged types. WindowID (at init if available, might be set later): %u", self.windowId); // self.windowId might not be set here yet.
[self registerForDraggedTypes:@[NSFilenamesPboardType]];
}
return self;
@ -22,47 +24,65 @@ extern void processDragItems(unsigned int windowId, char** arr, int length, int
// draggingEntered:
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
NSLog(@"WebviewDrag: draggingEntered called. WindowID: %u", self.windowId);
NSPasteboard *pasteboard = [sender draggingPasteboard];
if ([[pasteboard types] containsObject:NSFilenamesPboardType]) {
NSLog(@"WebviewDrag: draggingEntered - Found NSFilenamesPboardType. Firing EventWindowFileDraggingEntered.");
processWindowEvent(self.windowId, EventWindowFileDraggingEntered);
// Notify JS for hover effects
macosOnDragEnter(self.windowId);
return NSDragOperationCopy;
}
return NSDragOperationNone;
}
// draggingUpdated:
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
NSPasteboard *pasteboard = [sender draggingPasteboard];
if ([[pasteboard types] containsObject:NSFilenamesPboardType]) {
// Get the current mouse position
NSPoint dropPointInWindow = [sender draggingLocation];
NSPoint dropPointInView = [self convertPoint:dropPointInWindow fromView:nil];
// Get the window's content view height for coordinate conversion
NSView *contentView = [self.window contentView];
CGFloat contentHeight = contentView.frame.size.height;
int x = (int)dropPointInView.x;
int y = (int)(contentHeight - dropPointInView.y);
// Notify JS for hover effects
macosOnDragOver(self.windowId, x, y);
return NSDragOperationCopy;
}
NSLog(@"WebviewDrag: draggingEntered - NSFilenamesPboardType NOT found.");
return NSDragOperationNone;
}
// draggingExited:
- (void)draggingExited:(id<NSDraggingInfo>)sender {
NSLog(@"WebviewDrag: draggingExited called. WindowID: %u", self.windowId); // Added log
processWindowEvent(self.windowId, EventWindowFileDraggingExited);
// Notify JS to clean up hover effects
macosOnDragExit(self.windowId);
}
// prepareForDragOperation:
- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)sender {
NSLog(@"WebviewDrag: prepareForDragOperation called. WindowID: %u", self.windowId); // Added log
return YES;
}
// performDragOperation:
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
NSLog(@"WebviewDrag: performDragOperation called. WindowID: %u", self.windowId);
NSPasteboard *pasteboard = [sender draggingPasteboard];
processWindowEvent(self.windowId, EventWindowFileDraggingPerformed);
if ([[pasteboard types] containsObject:NSFilenamesPboardType]) {
NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
NSUInteger count = [files count];
NSLog(@"WebviewDrag: performDragOperation - File count: %lu", (unsigned long)count);
if (count == 0) {
NSLog(@"WebviewDrag: performDragOperation - No files found in pasteboard, though type was present.");
return NO;
}
char** cArray = (char**)malloc(count * sizeof(char*));
for (NSUInteger i = 0; i < count; i++) {
NSString* str = files[i];
NSLog(@"WebviewDrag: performDragOperation - File %lu: %@", (unsigned long)i, str);
cArray[i] = (char*)[str UTF8String];
}
@ -73,18 +93,14 @@ extern void processDragItems(unsigned int windowId, char** arr, int length, int
NSView *contentView = [self.window contentView];
CGFloat contentHeight = contentView.frame.size.height;
NSLog(@"WebviewDrag: Self height: %.2f, Content view height: %.2f", self.frame.size.height, contentHeight);
int x = (int)dropPointInView.x;
// Use the content view height for conversion
int y = (int)(contentHeight - dropPointInView.y);
processDragItems(self.windowId, cArray, (int)count, x, y);
free(cArray);
NSLog(@"WebviewDrag: performDragOperation - Returned from processDragItems.");
return YES;
}
NSLog(@"WebviewDrag: performDragOperation - NSFilenamesPboardType NOT found. Returning NO.");
return NO;
}

View file

@ -282,8 +282,10 @@ func (w *linuxWebviewWindow) run() {
w.window, w.webview, w.vbox = windowNew(app.application, w.gtkmenu, w.parent.id, w.parent.options.Linux.WebviewGpuPolicy)
app.registerWindow(w.window, w.parent.id) // record our mapping
w.connectSignals()
if w.parent.options.EnableDragAndDrop {
if w.parent.options.EnableFileDrop {
w.enableDND()
} else {
w.disableDND()
}
w.setTitle(w.parent.options.Title)
w.setIcon(app.icon)
@ -352,7 +354,10 @@ func (w *linuxWebviewWindow) run() {
})
w.parent.RegisterHook(events.Linux.WindowLoadFinished, func(e *WindowEvent) {
w.execJS(runtime.Core(globalApplication.impl.GetFlags(globalApplication.options)))
// Inject runtime core and EnableFileDrop flag together
js := runtime.Core(globalApplication.impl.GetFlags(globalApplication.options))
js += fmt.Sprintf("window._wails.flags.enableFileDrop=%v;", w.parent.options.EnableFileDrop)
w.execJS(js)
})
if w.parent.options.HTML != "" {
w.setHTML(w.parent.options.HTML)

View file

@ -104,8 +104,10 @@ type WebviewWindowOptions struct {
// ZoomControlEnabled will enable the zoom control.
ZoomControlEnabled bool
// EnableDragAndDrop will enable drag and drop.
EnableDragAndDrop bool
// EnableFileDrop enables drag and drop of files onto the window.
// When enabled, files dragged from the OS onto elements with the
// `data-file-drop-target` attribute will trigger a FilesDropped event.
EnableFileDrop bool
// OpenInspectorOnStartup will open the inspector when the window is first shown.
OpenInspectorOnStartup bool
@ -182,20 +184,6 @@ const (
/******* Windows Options *******/
type BackdropType int32
type DragEffect int32
const (
// DragEffectNone is used to indicate that the drop target cannot accept the data.
DragEffectNone DragEffect = 1
// DragEffectCopy is used to indicate that the data is copied to the drop target.
DragEffectCopy DragEffect = 2
// DragEffectMove is used to indicate that the data is removed from the drag source.
DragEffectMove DragEffect = 3
// DragEffectLink is used to indicate that a link to the original data is established.
DragEffectLink DragEffect = 4
// DragEffectScroll is used to indicate that the target can be scrolled while dragging to locate a drop position that is not currently visible in the target.
)
const (
Auto BackdropType = 0
@ -281,10 +269,6 @@ type WindowsWindow struct {
// Menu is the menu to use for the window.
Menu *Menu
// Drag Cursor Effects
OnEnterEffect DragEffect
OnOverEffect DragEffect
// Permissions map for WebView2. If empty, default permissions will be granted.
Permissions map[CoreWebView2PermissionKind]CoreWebView2PermissionState

View file

@ -82,21 +82,6 @@ func TestBackdropType_Constants(t *testing.T) {
}
}
func TestDragEffect_Constants(t *testing.T) {
if DragEffectNone != 1 {
t.Error("DragEffectNone should be 1")
}
if DragEffectCopy != 2 {
t.Error("DragEffectCopy should be 2")
}
if DragEffectMove != 3 {
t.Error("DragEffectMove should be 3")
}
if DragEffectLink != 4 {
t.Error("DragEffectLink should be 4")
}
}
func TestTheme_Constants(t *testing.T) {
if SystemDefault != 0 {
t.Error("SystemDefault should be 0")

View file

@ -10,7 +10,6 @@ import (
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
"unsafe"
@ -67,7 +66,6 @@ type windowsWebviewWindow struct {
resizeBorderWidth int32
resizeBorderHeight int32
focusingChromium bool
dropTarget *w32.DropTarget
onceDo sync.Once
// Window move debouncer
@ -721,9 +719,6 @@ func (w *windowsWebviewWindow) setRelativePosition(x int, y int) {
func (w *windowsWebviewWindow) destroy() {
w.parent.markAsDestroyed()
if w.dropTarget != nil {
w.dropTarget.Release()
}
// destroy the window
w32.DestroyWindow(w.hwnd)
}
@ -1977,66 +1972,20 @@ func (w *windowsWebviewWindow) setupChromium() {
}
}
if w.parent.options.EnableDragAndDrop {
if chromium.HasCapability(edge.AllowExternalDrop) {
err := chromium.AllowExternalDrag(false)
if err != nil {
globalApplication.handleFatalError(err)
}
}
// Initialize OLE for drag-and-drop operations
w32.OleInitialise()
w.dropTarget = w32.NewDropTarget()
w.dropTarget.OnDrop = func(files []string, x int, y int) {
w.parent.emit(events.Windows.WindowDragDrop)
globalApplication.debug("[DragDropDebug] Windows DropTarget OnDrop: Raw screen coordinates", "x", x, "y", y)
// Convert screen coordinates to window-relative coordinates first
// Windows DropTarget gives us screen coordinates, but we need window-relative coordinates
windowRect := w32.GetWindowRect(w.hwnd)
windowRelativeX := x - int(windowRect.Left)
windowRelativeY := y - int(windowRect.Top)
globalApplication.debug("[DragDropDebug] Windows DropTarget OnDrop: After screen-to-window conversion", "windowRelativeX", windowRelativeX, "windowRelativeY", windowRelativeY)
// Convert window-relative coordinates to webview-relative coordinates
webviewX, webviewY := w.convertWindowToWebviewCoordinates(windowRelativeX, windowRelativeY)
globalApplication.debug("[DragDropDebug] Windows DropTarget OnDrop: Final webview coordinates", "webviewX", webviewX, "webviewY", webviewY)
w.parent.InitiateFrontendDropProcessing(files, webviewX, webviewY)
}
if opts.OnEnterEffect != 0 {
w.dropTarget.OnEnterEffect = convertEffect(opts.OnEnterEffect)
}
if opts.OnOverEffect != 0 {
w.dropTarget.OnOverEffect = convertEffect(opts.OnOverEffect)
}
w.dropTarget.OnEnter = func() {
w.parent.emit(events.Windows.WindowDragEnter)
}
w.dropTarget.OnLeave = func() {
w.parent.emit(events.Windows.WindowDragLeave)
}
w.dropTarget.OnOver = func() {
w.parent.emit(events.Windows.WindowDragOver)
}
// Enumerate all the child windows for this window and register them as drop targets
w32.EnumChildWindows(w.hwnd, func(hwnd w32.HWND, lparam w32.LPARAM) w32.LRESULT {
// Check if the window class is "Chrome_RenderWidgetHostHWND"
// If it is, then we register it as a drop target
//windowName := w32.GetClassName(hwnd)
//println(windowName)
//if windowName == "Chrome_RenderWidgetHostHWND" {
err := w32.RegisterDragDrop(hwnd, w.dropTarget)
if err != nil && !errors.Is(err, syscall.Errno(w32.DRAGDROP_E_ALREADYREGISTERED)) {
globalApplication.error("error registering drag and drop: %w", err)
}
//}
return 1
})
}
// File drop handling on Windows:
// WebView2's AllowExternalDrop controls ALL drag-and-drop (both external file drops
// AND internal HTML5 drag-and-drop). We cannot disable it without breaking HTML5 DnD.
//
// When EnableFileDrop is true:
// - JS dragenter/dragover/drop events fire for external file drags
// - JS calls preventDefault() to stop the browser from navigating to the file
// - JS uses chrome.webview.postMessageWithAdditionalObjects to send file paths to Go
// - Go receives paths via processMessageWithAdditionalObjects
//
// When EnableFileDrop is false:
// - We cannot use AllowExternalDrag(false) as it breaks HTML5 internal drag-and-drop
// - JS runtime checks window._wails.flags.enableFileDrop and shows "no drop" cursor
// - The enableFileDrop flag is injected in navigationCompleted callback
err = chromium.PutIsGeneralAutofillEnabled(opts.GeneralAutofillEnabled)
if err != nil {
@ -2151,19 +2100,6 @@ func (w *windowsWebviewWindow) fullscreenChanged(
}
}
func convertEffect(effect DragEffect) w32.DWORD {
switch effect {
case DragEffectCopy:
return w32.DROPEFFECT_COPY
case DragEffectMove:
return w32.DROPEFFECT_MOVE
case DragEffectLink:
return w32.DROPEFFECT_LINK
default:
return w32.DROPEFFECT_NONE
}
}
func (w *windowsWebviewWindow) flash(enabled bool) {
w32.FlashWindow(w.hwnd, enabled)
}
@ -2176,6 +2112,10 @@ func (w *windowsWebviewWindow) navigationCompleted(
// Install the runtime core
w.execJS(runtime.Core(globalApplication.impl.GetFlags(globalApplication.options)))
// Set the EnableFileDrop flag for this window (Windows-specific)
// The JS runtime checks this before processing file drops
w.execJS(fmt.Sprintf("window._wails.flags.enableFileDrop = %v;", w.parent.options.EnableFileDrop))
// EmitEvent DomReady ApplicationEvent
windowEvents <- &windowEvent{EventID: uint(events.Windows.WebViewNavigationCompleted), WindowID: w.parent.id}
@ -2277,7 +2217,7 @@ func (w *windowsWebviewWindow) processMessageWithAdditionalObjects(
sender *edge.ICoreWebView2,
args *edge.ICoreWebView2WebMessageReceivedEventArgs,
) {
if strings.HasPrefix(message, "FilesDropped") {
if strings.HasPrefix(message, "file:drop:") {
objs, err := args.GetAdditionalObjects()
if err != nil {
globalApplication.handleError(err)
@ -2319,14 +2259,14 @@ func (w *windowsWebviewWindow) processMessageWithAdditionalObjects(
filenames = append(filenames, filepath)
}
// Extract X/Y coordinates from message - format should be "FilesDropped:x:y"
// Extract X/Y coordinates from message - format is "file:drop:x:y"
var x, y int
parts := strings.Split(message, ":")
if len(parts) >= 3 {
if parsedX, err := strconv.Atoi(parts[1]); err == nil {
if len(parts) >= 4 {
if parsedX, err := strconv.Atoi(parts[2]); err == nil {
x = parsedX
}
if parsedY, err := strconv.Atoi(parts[2]); err == nil {
if parsedY, err := strconv.Atoi(parts[3]); err == nil {
y = parsedY
}
}

View file

@ -21,7 +21,7 @@ type Window interface {
GetBorderSizes() *LRTB
GetScreen() (*Screen, error)
GetZoom() float64
HandleDragAndDropMessage(filenames []string, dropZone *DropZoneDetails)
handleDragAndDropMessage(filenames []string, dropTarget *DropTargetDetails)
InitiateFrontendDropProcessing(filenames []string, x int, y int)
HandleMessage(message string)
HandleWindowEvent(id uint)

View file

@ -32,7 +32,6 @@ type commonEvents struct {
WindowZoomIn WindowEventType
WindowZoomOut WindowEventType
WindowZoomReset WindowEventType
WindowDropZoneFilesDropped WindowEventType
}
func newCommonEvents() commonEvents {
@ -63,7 +62,6 @@ func newCommonEvents() commonEvents {
WindowZoomIn: 1047,
WindowZoomOut: 1048,
WindowZoomReset: 1049,
WindowDropZoneFilesDropped: 1050,
}
}
@ -85,17 +83,17 @@ type linuxEvents struct {
func newLinuxEvents() linuxEvents {
return linuxEvents{
ApplicationStartup: 1051,
SystemThemeChanged: 1052,
WindowDeleteEvent: 1053,
WindowDidMove: 1054,
WindowDidResize: 1055,
WindowFocusIn: 1056,
WindowFocusOut: 1057,
WindowLoadStarted: 1058,
WindowLoadRedirected: 1059,
WindowLoadCommitted: 1060,
WindowLoadFinished: 1061,
ApplicationStartup: 1050,
SystemThemeChanged: 1051,
WindowDeleteEvent: 1052,
WindowDidMove: 1053,
WindowDidResize: 1054,
WindowFocusIn: 1055,
WindowFocusOut: 1056,
WindowLoadStarted: 1057,
WindowLoadRedirected: 1058,
WindowLoadCommitted: 1059,
WindowLoadFinished: 1060,
}
}
@ -238,138 +236,138 @@ type macEvents struct {
func newMacEvents() macEvents {
return macEvents{
ApplicationDidBecomeActive: 1062,
ApplicationDidChangeBackingProperties: 1063,
ApplicationDidChangeEffectiveAppearance: 1064,
ApplicationDidChangeIcon: 1065,
ApplicationDidChangeOcclusionState: 1066,
ApplicationDidChangeScreenParameters: 1067,
ApplicationDidChangeStatusBarFrame: 1068,
ApplicationDidChangeStatusBarOrientation: 1069,
ApplicationDidChangeTheme: 1070,
ApplicationDidFinishLaunching: 1071,
ApplicationDidHide: 1072,
ApplicationDidResignActive: 1073,
ApplicationDidUnhide: 1074,
ApplicationDidUpdate: 1075,
ApplicationShouldHandleReopen: 1076,
ApplicationWillBecomeActive: 1077,
ApplicationWillFinishLaunching: 1078,
ApplicationWillHide: 1079,
ApplicationWillResignActive: 1080,
ApplicationWillTerminate: 1081,
ApplicationWillUnhide: 1082,
ApplicationWillUpdate: 1083,
MenuDidAddItem: 1084,
MenuDidBeginTracking: 1085,
MenuDidClose: 1086,
MenuDidDisplayItem: 1087,
MenuDidEndTracking: 1088,
MenuDidHighlightItem: 1089,
MenuDidOpen: 1090,
MenuDidPopUp: 1091,
MenuDidRemoveItem: 1092,
MenuDidSendAction: 1093,
MenuDidSendActionToItem: 1094,
MenuDidUpdate: 1095,
MenuWillAddItem: 1096,
MenuWillBeginTracking: 1097,
MenuWillDisplayItem: 1098,
MenuWillEndTracking: 1099,
MenuWillHighlightItem: 1100,
MenuWillOpen: 1101,
MenuWillPopUp: 1102,
MenuWillRemoveItem: 1103,
MenuWillSendAction: 1104,
MenuWillSendActionToItem: 1105,
MenuWillUpdate: 1106,
WebViewDidCommitNavigation: 1107,
WebViewDidFinishNavigation: 1108,
WebViewDidReceiveServerRedirectForProvisionalNavigation: 1109,
WebViewDidStartProvisionalNavigation: 1110,
WindowDidBecomeKey: 1111,
WindowDidBecomeMain: 1112,
WindowDidBeginSheet: 1113,
WindowDidChangeAlpha: 1114,
WindowDidChangeBackingLocation: 1115,
WindowDidChangeBackingProperties: 1116,
WindowDidChangeCollectionBehavior: 1117,
WindowDidChangeEffectiveAppearance: 1118,
WindowDidChangeOcclusionState: 1119,
WindowDidChangeOrderingMode: 1120,
WindowDidChangeScreen: 1121,
WindowDidChangeScreenParameters: 1122,
WindowDidChangeScreenProfile: 1123,
WindowDidChangeScreenSpace: 1124,
WindowDidChangeScreenSpaceProperties: 1125,
WindowDidChangeSharingType: 1126,
WindowDidChangeSpace: 1127,
WindowDidChangeSpaceOrderingMode: 1128,
WindowDidChangeTitle: 1129,
WindowDidChangeToolbar: 1130,
WindowDidDeminiaturize: 1131,
WindowDidEndSheet: 1132,
WindowDidEnterFullScreen: 1133,
WindowDidEnterVersionBrowser: 1134,
WindowDidExitFullScreen: 1135,
WindowDidExitVersionBrowser: 1136,
WindowDidExpose: 1137,
WindowDidFocus: 1138,
WindowDidMiniaturize: 1139,
WindowDidMove: 1140,
WindowDidOrderOffScreen: 1141,
WindowDidOrderOnScreen: 1142,
WindowDidResignKey: 1143,
WindowDidResignMain: 1144,
WindowDidResize: 1145,
WindowDidUpdate: 1146,
WindowDidUpdateAlpha: 1147,
WindowDidUpdateCollectionBehavior: 1148,
WindowDidUpdateCollectionProperties: 1149,
WindowDidUpdateShadow: 1150,
WindowDidUpdateTitle: 1151,
WindowDidUpdateToolbar: 1152,
WindowDidZoom: 1153,
WindowFileDraggingEntered: 1154,
WindowFileDraggingExited: 1155,
WindowFileDraggingPerformed: 1156,
WindowHide: 1157,
WindowMaximise: 1158,
WindowUnMaximise: 1159,
WindowMinimise: 1160,
WindowUnMinimise: 1161,
WindowShouldClose: 1162,
WindowShow: 1163,
WindowWillBecomeKey: 1164,
WindowWillBecomeMain: 1165,
WindowWillBeginSheet: 1166,
WindowWillChangeOrderingMode: 1167,
WindowWillClose: 1168,
WindowWillDeminiaturize: 1169,
WindowWillEnterFullScreen: 1170,
WindowWillEnterVersionBrowser: 1171,
WindowWillExitFullScreen: 1172,
WindowWillExitVersionBrowser: 1173,
WindowWillFocus: 1174,
WindowWillMiniaturize: 1175,
WindowWillMove: 1176,
WindowWillOrderOffScreen: 1177,
WindowWillOrderOnScreen: 1178,
WindowWillResignMain: 1179,
WindowWillResize: 1180,
WindowWillUnfocus: 1181,
WindowWillUpdate: 1182,
WindowWillUpdateAlpha: 1183,
WindowWillUpdateCollectionBehavior: 1184,
WindowWillUpdateCollectionProperties: 1185,
WindowWillUpdateShadow: 1186,
WindowWillUpdateTitle: 1187,
WindowWillUpdateToolbar: 1188,
WindowWillUpdateVisibility: 1189,
WindowWillUseStandardFrame: 1190,
WindowZoomIn: 1191,
WindowZoomOut: 1192,
WindowZoomReset: 1193,
ApplicationDidBecomeActive: 1061,
ApplicationDidChangeBackingProperties: 1062,
ApplicationDidChangeEffectiveAppearance: 1063,
ApplicationDidChangeIcon: 1064,
ApplicationDidChangeOcclusionState: 1065,
ApplicationDidChangeScreenParameters: 1066,
ApplicationDidChangeStatusBarFrame: 1067,
ApplicationDidChangeStatusBarOrientation: 1068,
ApplicationDidChangeTheme: 1069,
ApplicationDidFinishLaunching: 1070,
ApplicationDidHide: 1071,
ApplicationDidResignActive: 1072,
ApplicationDidUnhide: 1073,
ApplicationDidUpdate: 1074,
ApplicationShouldHandleReopen: 1075,
ApplicationWillBecomeActive: 1076,
ApplicationWillFinishLaunching: 1077,
ApplicationWillHide: 1078,
ApplicationWillResignActive: 1079,
ApplicationWillTerminate: 1080,
ApplicationWillUnhide: 1081,
ApplicationWillUpdate: 1082,
MenuDidAddItem: 1083,
MenuDidBeginTracking: 1084,
MenuDidClose: 1085,
MenuDidDisplayItem: 1086,
MenuDidEndTracking: 1087,
MenuDidHighlightItem: 1088,
MenuDidOpen: 1089,
MenuDidPopUp: 1090,
MenuDidRemoveItem: 1091,
MenuDidSendAction: 1092,
MenuDidSendActionToItem: 1093,
MenuDidUpdate: 1094,
MenuWillAddItem: 1095,
MenuWillBeginTracking: 1096,
MenuWillDisplayItem: 1097,
MenuWillEndTracking: 1098,
MenuWillHighlightItem: 1099,
MenuWillOpen: 1100,
MenuWillPopUp: 1101,
MenuWillRemoveItem: 1102,
MenuWillSendAction: 1103,
MenuWillSendActionToItem: 1104,
MenuWillUpdate: 1105,
WebViewDidCommitNavigation: 1106,
WebViewDidFinishNavigation: 1107,
WebViewDidReceiveServerRedirectForProvisionalNavigation: 1108,
WebViewDidStartProvisionalNavigation: 1109,
WindowDidBecomeKey: 1110,
WindowDidBecomeMain: 1111,
WindowDidBeginSheet: 1112,
WindowDidChangeAlpha: 1113,
WindowDidChangeBackingLocation: 1114,
WindowDidChangeBackingProperties: 1115,
WindowDidChangeCollectionBehavior: 1116,
WindowDidChangeEffectiveAppearance: 1117,
WindowDidChangeOcclusionState: 1118,
WindowDidChangeOrderingMode: 1119,
WindowDidChangeScreen: 1120,
WindowDidChangeScreenParameters: 1121,
WindowDidChangeScreenProfile: 1122,
WindowDidChangeScreenSpace: 1123,
WindowDidChangeScreenSpaceProperties: 1124,
WindowDidChangeSharingType: 1125,
WindowDidChangeSpace: 1126,
WindowDidChangeSpaceOrderingMode: 1127,
WindowDidChangeTitle: 1128,
WindowDidChangeToolbar: 1129,
WindowDidDeminiaturize: 1130,
WindowDidEndSheet: 1131,
WindowDidEnterFullScreen: 1132,
WindowDidEnterVersionBrowser: 1133,
WindowDidExitFullScreen: 1134,
WindowDidExitVersionBrowser: 1135,
WindowDidExpose: 1136,
WindowDidFocus: 1137,
WindowDidMiniaturize: 1138,
WindowDidMove: 1139,
WindowDidOrderOffScreen: 1140,
WindowDidOrderOnScreen: 1141,
WindowDidResignKey: 1142,
WindowDidResignMain: 1143,
WindowDidResize: 1144,
WindowDidUpdate: 1145,
WindowDidUpdateAlpha: 1146,
WindowDidUpdateCollectionBehavior: 1147,
WindowDidUpdateCollectionProperties: 1148,
WindowDidUpdateShadow: 1149,
WindowDidUpdateTitle: 1150,
WindowDidUpdateToolbar: 1151,
WindowDidZoom: 1152,
WindowFileDraggingEntered: 1153,
WindowFileDraggingExited: 1154,
WindowFileDraggingPerformed: 1155,
WindowHide: 1156,
WindowMaximise: 1157,
WindowUnMaximise: 1158,
WindowMinimise: 1159,
WindowUnMinimise: 1160,
WindowShouldClose: 1161,
WindowShow: 1162,
WindowWillBecomeKey: 1163,
WindowWillBecomeMain: 1164,
WindowWillBeginSheet: 1165,
WindowWillChangeOrderingMode: 1166,
WindowWillClose: 1167,
WindowWillDeminiaturize: 1168,
WindowWillEnterFullScreen: 1169,
WindowWillEnterVersionBrowser: 1170,
WindowWillExitFullScreen: 1171,
WindowWillExitVersionBrowser: 1172,
WindowWillFocus: 1173,
WindowWillMiniaturize: 1174,
WindowWillMove: 1175,
WindowWillOrderOffScreen: 1176,
WindowWillOrderOnScreen: 1177,
WindowWillResignMain: 1178,
WindowWillResize: 1179,
WindowWillUnfocus: 1180,
WindowWillUpdate: 1181,
WindowWillUpdateAlpha: 1182,
WindowWillUpdateCollectionBehavior: 1183,
WindowWillUpdateCollectionProperties: 1184,
WindowWillUpdateShadow: 1185,
WindowWillUpdateTitle: 1186,
WindowWillUpdateToolbar: 1187,
WindowWillUpdateVisibility: 1188,
WindowWillUseStandardFrame: 1189,
WindowZoomIn: 1190,
WindowZoomOut: 1191,
WindowZoomReset: 1192,
}
}
@ -424,50 +422,50 @@ type windowsEvents struct {
func newWindowsEvents() windowsEvents {
return windowsEvents{
APMPowerSettingChange: 1194,
APMPowerStatusChange: 1195,
APMResumeAutomatic: 1196,
APMResumeSuspend: 1197,
APMSuspend: 1198,
ApplicationStarted: 1199,
SystemThemeChanged: 1200,
WebViewNavigationCompleted: 1201,
WindowActive: 1202,
WindowBackgroundErase: 1203,
WindowClickActive: 1204,
WindowClosing: 1205,
WindowDidMove: 1206,
WindowDidResize: 1207,
WindowDPIChanged: 1208,
WindowDragDrop: 1209,
WindowDragEnter: 1210,
WindowDragLeave: 1211,
WindowDragOver: 1212,
WindowEndMove: 1213,
WindowEndResize: 1214,
WindowFullscreen: 1215,
WindowHide: 1216,
WindowInactive: 1217,
WindowKeyDown: 1218,
WindowKeyUp: 1219,
WindowKillFocus: 1220,
WindowNonClientHit: 1221,
WindowNonClientMouseDown: 1222,
WindowNonClientMouseLeave: 1223,
WindowNonClientMouseMove: 1224,
WindowNonClientMouseUp: 1225,
WindowPaint: 1226,
WindowRestore: 1227,
WindowSetFocus: 1228,
WindowShow: 1229,
WindowStartMove: 1230,
WindowStartResize: 1231,
WindowUnFullscreen: 1232,
WindowZOrderChanged: 1233,
WindowMinimise: 1234,
WindowUnMinimise: 1235,
WindowMaximise: 1236,
WindowUnMaximise: 1237,
APMPowerSettingChange: 1193,
APMPowerStatusChange: 1194,
APMResumeAutomatic: 1195,
APMResumeSuspend: 1196,
APMSuspend: 1197,
ApplicationStarted: 1198,
SystemThemeChanged: 1199,
WebViewNavigationCompleted: 1200,
WindowActive: 1201,
WindowBackgroundErase: 1202,
WindowClickActive: 1203,
WindowClosing: 1204,
WindowDidMove: 1205,
WindowDidResize: 1206,
WindowDPIChanged: 1207,
WindowDragDrop: 1208,
WindowDragEnter: 1209,
WindowDragLeave: 1210,
WindowDragOver: 1211,
WindowEndMove: 1212,
WindowEndResize: 1213,
WindowFullscreen: 1214,
WindowHide: 1215,
WindowInactive: 1216,
WindowKeyDown: 1217,
WindowKeyUp: 1218,
WindowKillFocus: 1219,
WindowNonClientHit: 1220,
WindowNonClientMouseDown: 1221,
WindowNonClientMouseLeave: 1222,
WindowNonClientMouseMove: 1223,
WindowNonClientMouseUp: 1224,
WindowPaint: 1225,
WindowRestore: 1226,
WindowSetFocus: 1227,
WindowShow: 1228,
WindowStartMove: 1229,
WindowStartResize: 1230,
WindowUnFullscreen: 1231,
WindowZOrderChanged: 1232,
WindowMinimise: 1233,
WindowUnMinimise: 1234,
WindowMaximise: 1235,
WindowUnMaximise: 1236,
}
}
@ -500,28 +498,28 @@ type iosEvents struct {
func newIOSEvents() iosEvents {
return iosEvents{
ApplicationDidBecomeActive: 1238,
ApplicationDidEnterBackground: 1239,
ApplicationDidFinishLaunching: 1240,
ApplicationDidReceiveMemoryWarning: 1241,
ApplicationWillEnterForeground: 1242,
ApplicationWillResignActive: 1243,
ApplicationWillTerminate: 1244,
WindowDidLoad: 1245,
WindowWillAppear: 1246,
WindowDidAppear: 1247,
WindowWillDisappear: 1248,
WindowDidDisappear: 1249,
WindowSafeAreaInsetsChanged: 1250,
WindowOrientationChanged: 1251,
WindowTouchBegan: 1252,
WindowTouchMoved: 1253,
WindowTouchEnded: 1254,
WindowTouchCancelled: 1255,
WebViewDidStartNavigation: 1256,
WebViewDidFinishNavigation: 1257,
WebViewDidFailNavigation: 1258,
WebViewDecidePolicyForNavigationAction: 1259,
ApplicationDidBecomeActive: 1237,
ApplicationDidEnterBackground: 1238,
ApplicationDidFinishLaunching: 1239,
ApplicationDidReceiveMemoryWarning: 1240,
ApplicationWillEnterForeground: 1241,
ApplicationWillResignActive: 1242,
ApplicationWillTerminate: 1243,
WindowDidLoad: 1244,
WindowWillAppear: 1245,
WindowDidAppear: 1246,
WindowWillDisappear: 1247,
WindowDidDisappear: 1248,
WindowSafeAreaInsetsChanged: 1249,
WindowOrientationChanged: 1250,
WindowTouchBegan: 1251,
WindowTouchMoved: 1252,
WindowTouchEnded: 1253,
WindowTouchCancelled: 1254,
WebViewDidStartNavigation: 1255,
WebViewDidFinishNavigation: 1256,
WebViewDidFailNavigation: 1257,
WebViewDecidePolicyForNavigationAction: 1258,
}
}
@ -556,214 +554,213 @@ var eventToJS = map[uint]string{
1047: "common:WindowZoomIn",
1048: "common:WindowZoomOut",
1049: "common:WindowZoomReset",
1050: "common:WindowDropZoneFilesDropped",
1051: "linux:ApplicationStartup",
1052: "linux:SystemThemeChanged",
1053: "linux:WindowDeleteEvent",
1054: "linux:WindowDidMove",
1055: "linux:WindowDidResize",
1056: "linux:WindowFocusIn",
1057: "linux:WindowFocusOut",
1058: "linux:WindowLoadStarted",
1059: "linux:WindowLoadRedirected",
1060: "linux:WindowLoadCommitted",
1061: "linux:WindowLoadFinished",
1062: "mac:ApplicationDidBecomeActive",
1063: "mac:ApplicationDidChangeBackingProperties",
1064: "mac:ApplicationDidChangeEffectiveAppearance",
1065: "mac:ApplicationDidChangeIcon",
1066: "mac:ApplicationDidChangeOcclusionState",
1067: "mac:ApplicationDidChangeScreenParameters",
1068: "mac:ApplicationDidChangeStatusBarFrame",
1069: "mac:ApplicationDidChangeStatusBarOrientation",
1070: "mac:ApplicationDidChangeTheme",
1071: "mac:ApplicationDidFinishLaunching",
1072: "mac:ApplicationDidHide",
1073: "mac:ApplicationDidResignActive",
1074: "mac:ApplicationDidUnhide",
1075: "mac:ApplicationDidUpdate",
1076: "mac:ApplicationShouldHandleReopen",
1077: "mac:ApplicationWillBecomeActive",
1078: "mac:ApplicationWillFinishLaunching",
1079: "mac:ApplicationWillHide",
1080: "mac:ApplicationWillResignActive",
1081: "mac:ApplicationWillTerminate",
1082: "mac:ApplicationWillUnhide",
1083: "mac:ApplicationWillUpdate",
1084: "mac:MenuDidAddItem",
1085: "mac:MenuDidBeginTracking",
1086: "mac:MenuDidClose",
1087: "mac:MenuDidDisplayItem",
1088: "mac:MenuDidEndTracking",
1089: "mac:MenuDidHighlightItem",
1090: "mac:MenuDidOpen",
1091: "mac:MenuDidPopUp",
1092: "mac:MenuDidRemoveItem",
1093: "mac:MenuDidSendAction",
1094: "mac:MenuDidSendActionToItem",
1095: "mac:MenuDidUpdate",
1096: "mac:MenuWillAddItem",
1097: "mac:MenuWillBeginTracking",
1098: "mac:MenuWillDisplayItem",
1099: "mac:MenuWillEndTracking",
1100: "mac:MenuWillHighlightItem",
1101: "mac:MenuWillOpen",
1102: "mac:MenuWillPopUp",
1103: "mac:MenuWillRemoveItem",
1104: "mac:MenuWillSendAction",
1105: "mac:MenuWillSendActionToItem",
1106: "mac:MenuWillUpdate",
1107: "mac:WebViewDidCommitNavigation",
1108: "mac:WebViewDidFinishNavigation",
1109: "mac:WebViewDidReceiveServerRedirectForProvisionalNavigation",
1110: "mac:WebViewDidStartProvisionalNavigation",
1111: "mac:WindowDidBecomeKey",
1112: "mac:WindowDidBecomeMain",
1113: "mac:WindowDidBeginSheet",
1114: "mac:WindowDidChangeAlpha",
1115: "mac:WindowDidChangeBackingLocation",
1116: "mac:WindowDidChangeBackingProperties",
1117: "mac:WindowDidChangeCollectionBehavior",
1118: "mac:WindowDidChangeEffectiveAppearance",
1119: "mac:WindowDidChangeOcclusionState",
1120: "mac:WindowDidChangeOrderingMode",
1121: "mac:WindowDidChangeScreen",
1122: "mac:WindowDidChangeScreenParameters",
1123: "mac:WindowDidChangeScreenProfile",
1124: "mac:WindowDidChangeScreenSpace",
1125: "mac:WindowDidChangeScreenSpaceProperties",
1126: "mac:WindowDidChangeSharingType",
1127: "mac:WindowDidChangeSpace",
1128: "mac:WindowDidChangeSpaceOrderingMode",
1129: "mac:WindowDidChangeTitle",
1130: "mac:WindowDidChangeToolbar",
1131: "mac:WindowDidDeminiaturize",
1132: "mac:WindowDidEndSheet",
1133: "mac:WindowDidEnterFullScreen",
1134: "mac:WindowDidEnterVersionBrowser",
1135: "mac:WindowDidExitFullScreen",
1136: "mac:WindowDidExitVersionBrowser",
1137: "mac:WindowDidExpose",
1138: "mac:WindowDidFocus",
1139: "mac:WindowDidMiniaturize",
1140: "mac:WindowDidMove",
1141: "mac:WindowDidOrderOffScreen",
1142: "mac:WindowDidOrderOnScreen",
1143: "mac:WindowDidResignKey",
1144: "mac:WindowDidResignMain",
1145: "mac:WindowDidResize",
1146: "mac:WindowDidUpdate",
1147: "mac:WindowDidUpdateAlpha",
1148: "mac:WindowDidUpdateCollectionBehavior",
1149: "mac:WindowDidUpdateCollectionProperties",
1150: "mac:WindowDidUpdateShadow",
1151: "mac:WindowDidUpdateTitle",
1152: "mac:WindowDidUpdateToolbar",
1153: "mac:WindowDidZoom",
1154: "mac:WindowFileDraggingEntered",
1155: "mac:WindowFileDraggingExited",
1156: "mac:WindowFileDraggingPerformed",
1157: "mac:WindowHide",
1158: "mac:WindowMaximise",
1159: "mac:WindowUnMaximise",
1160: "mac:WindowMinimise",
1161: "mac:WindowUnMinimise",
1162: "mac:WindowShouldClose",
1163: "mac:WindowShow",
1164: "mac:WindowWillBecomeKey",
1165: "mac:WindowWillBecomeMain",
1166: "mac:WindowWillBeginSheet",
1167: "mac:WindowWillChangeOrderingMode",
1168: "mac:WindowWillClose",
1169: "mac:WindowWillDeminiaturize",
1170: "mac:WindowWillEnterFullScreen",
1171: "mac:WindowWillEnterVersionBrowser",
1172: "mac:WindowWillExitFullScreen",
1173: "mac:WindowWillExitVersionBrowser",
1174: "mac:WindowWillFocus",
1175: "mac:WindowWillMiniaturize",
1176: "mac:WindowWillMove",
1177: "mac:WindowWillOrderOffScreen",
1178: "mac:WindowWillOrderOnScreen",
1179: "mac:WindowWillResignMain",
1180: "mac:WindowWillResize",
1181: "mac:WindowWillUnfocus",
1182: "mac:WindowWillUpdate",
1183: "mac:WindowWillUpdateAlpha",
1184: "mac:WindowWillUpdateCollectionBehavior",
1185: "mac:WindowWillUpdateCollectionProperties",
1186: "mac:WindowWillUpdateShadow",
1187: "mac:WindowWillUpdateTitle",
1188: "mac:WindowWillUpdateToolbar",
1189: "mac:WindowWillUpdateVisibility",
1190: "mac:WindowWillUseStandardFrame",
1191: "mac:WindowZoomIn",
1192: "mac:WindowZoomOut",
1193: "mac:WindowZoomReset",
1194: "windows:APMPowerSettingChange",
1195: "windows:APMPowerStatusChange",
1196: "windows:APMResumeAutomatic",
1197: "windows:APMResumeSuspend",
1198: "windows:APMSuspend",
1199: "windows:ApplicationStarted",
1200: "windows:SystemThemeChanged",
1201: "windows:WebViewNavigationCompleted",
1202: "windows:WindowActive",
1203: "windows:WindowBackgroundErase",
1204: "windows:WindowClickActive",
1205: "windows:WindowClosing",
1206: "windows:WindowDidMove",
1207: "windows:WindowDidResize",
1208: "windows:WindowDPIChanged",
1209: "windows:WindowDragDrop",
1210: "windows:WindowDragEnter",
1211: "windows:WindowDragLeave",
1212: "windows:WindowDragOver",
1213: "windows:WindowEndMove",
1214: "windows:WindowEndResize",
1215: "windows:WindowFullscreen",
1216: "windows:WindowHide",
1217: "windows:WindowInactive",
1218: "windows:WindowKeyDown",
1219: "windows:WindowKeyUp",
1220: "windows:WindowKillFocus",
1221: "windows:WindowNonClientHit",
1222: "windows:WindowNonClientMouseDown",
1223: "windows:WindowNonClientMouseLeave",
1224: "windows:WindowNonClientMouseMove",
1225: "windows:WindowNonClientMouseUp",
1226: "windows:WindowPaint",
1227: "windows:WindowRestore",
1228: "windows:WindowSetFocus",
1229: "windows:WindowShow",
1230: "windows:WindowStartMove",
1231: "windows:WindowStartResize",
1232: "windows:WindowUnFullscreen",
1233: "windows:WindowZOrderChanged",
1234: "windows:WindowMinimise",
1235: "windows:WindowUnMinimise",
1236: "windows:WindowMaximise",
1237: "windows:WindowUnMaximise",
1238: "ios:ApplicationDidBecomeActive",
1239: "ios:ApplicationDidEnterBackground",
1240: "ios:ApplicationDidFinishLaunching",
1241: "ios:ApplicationDidReceiveMemoryWarning",
1242: "ios:ApplicationWillEnterForeground",
1243: "ios:ApplicationWillResignActive",
1244: "ios:ApplicationWillTerminate",
1245: "ios:WindowDidLoad",
1246: "ios:WindowWillAppear",
1247: "ios:WindowDidAppear",
1248: "ios:WindowWillDisappear",
1249: "ios:WindowDidDisappear",
1250: "ios:WindowSafeAreaInsetsChanged",
1251: "ios:WindowOrientationChanged",
1252: "ios:WindowTouchBegan",
1253: "ios:WindowTouchMoved",
1254: "ios:WindowTouchEnded",
1255: "ios:WindowTouchCancelled",
1256: "ios:WebViewDidStartNavigation",
1257: "ios:WebViewDidFinishNavigation",
1258: "ios:WebViewDidFailNavigation",
1259: "ios:WebViewDecidePolicyForNavigationAction",
1050: "linux:ApplicationStartup",
1051: "linux:SystemThemeChanged",
1052: "linux:WindowDeleteEvent",
1053: "linux:WindowDidMove",
1054: "linux:WindowDidResize",
1055: "linux:WindowFocusIn",
1056: "linux:WindowFocusOut",
1057: "linux:WindowLoadStarted",
1058: "linux:WindowLoadRedirected",
1059: "linux:WindowLoadCommitted",
1060: "linux:WindowLoadFinished",
1061: "mac:ApplicationDidBecomeActive",
1062: "mac:ApplicationDidChangeBackingProperties",
1063: "mac:ApplicationDidChangeEffectiveAppearance",
1064: "mac:ApplicationDidChangeIcon",
1065: "mac:ApplicationDidChangeOcclusionState",
1066: "mac:ApplicationDidChangeScreenParameters",
1067: "mac:ApplicationDidChangeStatusBarFrame",
1068: "mac:ApplicationDidChangeStatusBarOrientation",
1069: "mac:ApplicationDidChangeTheme",
1070: "mac:ApplicationDidFinishLaunching",
1071: "mac:ApplicationDidHide",
1072: "mac:ApplicationDidResignActive",
1073: "mac:ApplicationDidUnhide",
1074: "mac:ApplicationDidUpdate",
1075: "mac:ApplicationShouldHandleReopen",
1076: "mac:ApplicationWillBecomeActive",
1077: "mac:ApplicationWillFinishLaunching",
1078: "mac:ApplicationWillHide",
1079: "mac:ApplicationWillResignActive",
1080: "mac:ApplicationWillTerminate",
1081: "mac:ApplicationWillUnhide",
1082: "mac:ApplicationWillUpdate",
1083: "mac:MenuDidAddItem",
1084: "mac:MenuDidBeginTracking",
1085: "mac:MenuDidClose",
1086: "mac:MenuDidDisplayItem",
1087: "mac:MenuDidEndTracking",
1088: "mac:MenuDidHighlightItem",
1089: "mac:MenuDidOpen",
1090: "mac:MenuDidPopUp",
1091: "mac:MenuDidRemoveItem",
1092: "mac:MenuDidSendAction",
1093: "mac:MenuDidSendActionToItem",
1094: "mac:MenuDidUpdate",
1095: "mac:MenuWillAddItem",
1096: "mac:MenuWillBeginTracking",
1097: "mac:MenuWillDisplayItem",
1098: "mac:MenuWillEndTracking",
1099: "mac:MenuWillHighlightItem",
1100: "mac:MenuWillOpen",
1101: "mac:MenuWillPopUp",
1102: "mac:MenuWillRemoveItem",
1103: "mac:MenuWillSendAction",
1104: "mac:MenuWillSendActionToItem",
1105: "mac:MenuWillUpdate",
1106: "mac:WebViewDidCommitNavigation",
1107: "mac:WebViewDidFinishNavigation",
1108: "mac:WebViewDidReceiveServerRedirectForProvisionalNavigation",
1109: "mac:WebViewDidStartProvisionalNavigation",
1110: "mac:WindowDidBecomeKey",
1111: "mac:WindowDidBecomeMain",
1112: "mac:WindowDidBeginSheet",
1113: "mac:WindowDidChangeAlpha",
1114: "mac:WindowDidChangeBackingLocation",
1115: "mac:WindowDidChangeBackingProperties",
1116: "mac:WindowDidChangeCollectionBehavior",
1117: "mac:WindowDidChangeEffectiveAppearance",
1118: "mac:WindowDidChangeOcclusionState",
1119: "mac:WindowDidChangeOrderingMode",
1120: "mac:WindowDidChangeScreen",
1121: "mac:WindowDidChangeScreenParameters",
1122: "mac:WindowDidChangeScreenProfile",
1123: "mac:WindowDidChangeScreenSpace",
1124: "mac:WindowDidChangeScreenSpaceProperties",
1125: "mac:WindowDidChangeSharingType",
1126: "mac:WindowDidChangeSpace",
1127: "mac:WindowDidChangeSpaceOrderingMode",
1128: "mac:WindowDidChangeTitle",
1129: "mac:WindowDidChangeToolbar",
1130: "mac:WindowDidDeminiaturize",
1131: "mac:WindowDidEndSheet",
1132: "mac:WindowDidEnterFullScreen",
1133: "mac:WindowDidEnterVersionBrowser",
1134: "mac:WindowDidExitFullScreen",
1135: "mac:WindowDidExitVersionBrowser",
1136: "mac:WindowDidExpose",
1137: "mac:WindowDidFocus",
1138: "mac:WindowDidMiniaturize",
1139: "mac:WindowDidMove",
1140: "mac:WindowDidOrderOffScreen",
1141: "mac:WindowDidOrderOnScreen",
1142: "mac:WindowDidResignKey",
1143: "mac:WindowDidResignMain",
1144: "mac:WindowDidResize",
1145: "mac:WindowDidUpdate",
1146: "mac:WindowDidUpdateAlpha",
1147: "mac:WindowDidUpdateCollectionBehavior",
1148: "mac:WindowDidUpdateCollectionProperties",
1149: "mac:WindowDidUpdateShadow",
1150: "mac:WindowDidUpdateTitle",
1151: "mac:WindowDidUpdateToolbar",
1152: "mac:WindowDidZoom",
1153: "mac:WindowFileDraggingEntered",
1154: "mac:WindowFileDraggingExited",
1155: "mac:WindowFileDraggingPerformed",
1156: "mac:WindowHide",
1157: "mac:WindowMaximise",
1158: "mac:WindowUnMaximise",
1159: "mac:WindowMinimise",
1160: "mac:WindowUnMinimise",
1161: "mac:WindowShouldClose",
1162: "mac:WindowShow",
1163: "mac:WindowWillBecomeKey",
1164: "mac:WindowWillBecomeMain",
1165: "mac:WindowWillBeginSheet",
1166: "mac:WindowWillChangeOrderingMode",
1167: "mac:WindowWillClose",
1168: "mac:WindowWillDeminiaturize",
1169: "mac:WindowWillEnterFullScreen",
1170: "mac:WindowWillEnterVersionBrowser",
1171: "mac:WindowWillExitFullScreen",
1172: "mac:WindowWillExitVersionBrowser",
1173: "mac:WindowWillFocus",
1174: "mac:WindowWillMiniaturize",
1175: "mac:WindowWillMove",
1176: "mac:WindowWillOrderOffScreen",
1177: "mac:WindowWillOrderOnScreen",
1178: "mac:WindowWillResignMain",
1179: "mac:WindowWillResize",
1180: "mac:WindowWillUnfocus",
1181: "mac:WindowWillUpdate",
1182: "mac:WindowWillUpdateAlpha",
1183: "mac:WindowWillUpdateCollectionBehavior",
1184: "mac:WindowWillUpdateCollectionProperties",
1185: "mac:WindowWillUpdateShadow",
1186: "mac:WindowWillUpdateTitle",
1187: "mac:WindowWillUpdateToolbar",
1188: "mac:WindowWillUpdateVisibility",
1189: "mac:WindowWillUseStandardFrame",
1190: "mac:WindowZoomIn",
1191: "mac:WindowZoomOut",
1192: "mac:WindowZoomReset",
1193: "windows:APMPowerSettingChange",
1194: "windows:APMPowerStatusChange",
1195: "windows:APMResumeAutomatic",
1196: "windows:APMResumeSuspend",
1197: "windows:APMSuspend",
1198: "windows:ApplicationStarted",
1199: "windows:SystemThemeChanged",
1200: "windows:WebViewNavigationCompleted",
1201: "windows:WindowActive",
1202: "windows:WindowBackgroundErase",
1203: "windows:WindowClickActive",
1204: "windows:WindowClosing",
1205: "windows:WindowDidMove",
1206: "windows:WindowDidResize",
1207: "windows:WindowDPIChanged",
1208: "windows:WindowDragDrop",
1209: "windows:WindowDragEnter",
1210: "windows:WindowDragLeave",
1211: "windows:WindowDragOver",
1212: "windows:WindowEndMove",
1213: "windows:WindowEndResize",
1214: "windows:WindowFullscreen",
1215: "windows:WindowHide",
1216: "windows:WindowInactive",
1217: "windows:WindowKeyDown",
1218: "windows:WindowKeyUp",
1219: "windows:WindowKillFocus",
1220: "windows:WindowNonClientHit",
1221: "windows:WindowNonClientMouseDown",
1222: "windows:WindowNonClientMouseLeave",
1223: "windows:WindowNonClientMouseMove",
1224: "windows:WindowNonClientMouseUp",
1225: "windows:WindowPaint",
1226: "windows:WindowRestore",
1227: "windows:WindowSetFocus",
1228: "windows:WindowShow",
1229: "windows:WindowStartMove",
1230: "windows:WindowStartResize",
1231: "windows:WindowUnFullscreen",
1232: "windows:WindowZOrderChanged",
1233: "windows:WindowMinimise",
1234: "windows:WindowUnMinimise",
1235: "windows:WindowMaximise",
1236: "windows:WindowUnMaximise",
1237: "ios:ApplicationDidBecomeActive",
1238: "ios:ApplicationDidEnterBackground",
1239: "ios:ApplicationDidFinishLaunching",
1240: "ios:ApplicationDidReceiveMemoryWarning",
1241: "ios:ApplicationWillEnterForeground",
1242: "ios:ApplicationWillResignActive",
1243: "ios:ApplicationWillTerminate",
1244: "ios:WindowDidLoad",
1245: "ios:WindowWillAppear",
1246: "ios:WindowDidAppear",
1247: "ios:WindowWillDisappear",
1248: "ios:WindowDidDisappear",
1249: "ios:WindowSafeAreaInsetsChanged",
1250: "ios:WindowOrientationChanged",
1251: "ios:WindowTouchBegan",
1252: "ios:WindowTouchMoved",
1253: "ios:WindowTouchEnded",
1254: "ios:WindowTouchCancelled",
1255: "ios:WebViewDidStartNavigation",
1256: "ios:WebViewDidFinishNavigation",
1257: "ios:WebViewDidFailNavigation",
1258: "ios:WebViewDecidePolicyForNavigationAction",
}

View file

@ -24,7 +24,6 @@ common:WindowZoom
common:WindowZoomIn
common:WindowZoomOut
common:WindowZoomReset
common:WindowDropZoneFilesDropped
linux:ApplicationStartup
linux:SystemThemeChanged
linux:WindowDeleteEvent

View file

@ -6,140 +6,140 @@
extern void processApplicationEvent(unsigned int, void* data);
extern void processWindowEvent(unsigned int, unsigned int);
#define EventApplicationDidBecomeActive 1062
#define EventApplicationDidChangeBackingProperties 1063
#define EventApplicationDidChangeEffectiveAppearance 1064
#define EventApplicationDidChangeIcon 1065
#define EventApplicationDidChangeOcclusionState 1066
#define EventApplicationDidChangeScreenParameters 1067
#define EventApplicationDidChangeStatusBarFrame 1068
#define EventApplicationDidChangeStatusBarOrientation 1069
#define EventApplicationDidChangeTheme 1070
#define EventApplicationDidFinishLaunching 1071
#define EventApplicationDidHide 1072
#define EventApplicationDidResignActive 1073
#define EventApplicationDidUnhide 1074
#define EventApplicationDidUpdate 1075
#define EventApplicationShouldHandleReopen 1076
#define EventApplicationWillBecomeActive 1077
#define EventApplicationWillFinishLaunching 1078
#define EventApplicationWillHide 1079
#define EventApplicationWillResignActive 1080
#define EventApplicationWillTerminate 1081
#define EventApplicationWillUnhide 1082
#define EventApplicationWillUpdate 1083
#define EventMenuDidAddItem 1084
#define EventMenuDidBeginTracking 1085
#define EventMenuDidClose 1086
#define EventMenuDidDisplayItem 1087
#define EventMenuDidEndTracking 1088
#define EventMenuDidHighlightItem 1089
#define EventMenuDidOpen 1090
#define EventMenuDidPopUp 1091
#define EventMenuDidRemoveItem 1092
#define EventMenuDidSendAction 1093
#define EventMenuDidSendActionToItem 1094
#define EventMenuDidUpdate 1095
#define EventMenuWillAddItem 1096
#define EventMenuWillBeginTracking 1097
#define EventMenuWillDisplayItem 1098
#define EventMenuWillEndTracking 1099
#define EventMenuWillHighlightItem 1100
#define EventMenuWillOpen 1101
#define EventMenuWillPopUp 1102
#define EventMenuWillRemoveItem 1103
#define EventMenuWillSendAction 1104
#define EventMenuWillSendActionToItem 1105
#define EventMenuWillUpdate 1106
#define EventWebViewDidCommitNavigation 1107
#define EventWebViewDidFinishNavigation 1108
#define EventWebViewDidReceiveServerRedirectForProvisionalNavigation 1109
#define EventWebViewDidStartProvisionalNavigation 1110
#define EventWindowDidBecomeKey 1111
#define EventWindowDidBecomeMain 1112
#define EventWindowDidBeginSheet 1113
#define EventWindowDidChangeAlpha 1114
#define EventWindowDidChangeBackingLocation 1115
#define EventWindowDidChangeBackingProperties 1116
#define EventWindowDidChangeCollectionBehavior 1117
#define EventWindowDidChangeEffectiveAppearance 1118
#define EventWindowDidChangeOcclusionState 1119
#define EventWindowDidChangeOrderingMode 1120
#define EventWindowDidChangeScreen 1121
#define EventWindowDidChangeScreenParameters 1122
#define EventWindowDidChangeScreenProfile 1123
#define EventWindowDidChangeScreenSpace 1124
#define EventWindowDidChangeScreenSpaceProperties 1125
#define EventWindowDidChangeSharingType 1126
#define EventWindowDidChangeSpace 1127
#define EventWindowDidChangeSpaceOrderingMode 1128
#define EventWindowDidChangeTitle 1129
#define EventWindowDidChangeToolbar 1130
#define EventWindowDidDeminiaturize 1131
#define EventWindowDidEndSheet 1132
#define EventWindowDidEnterFullScreen 1133
#define EventWindowDidEnterVersionBrowser 1134
#define EventWindowDidExitFullScreen 1135
#define EventWindowDidExitVersionBrowser 1136
#define EventWindowDidExpose 1137
#define EventWindowDidFocus 1138
#define EventWindowDidMiniaturize 1139
#define EventWindowDidMove 1140
#define EventWindowDidOrderOffScreen 1141
#define EventWindowDidOrderOnScreen 1142
#define EventWindowDidResignKey 1143
#define EventWindowDidResignMain 1144
#define EventWindowDidResize 1145
#define EventWindowDidUpdate 1146
#define EventWindowDidUpdateAlpha 1147
#define EventWindowDidUpdateCollectionBehavior 1148
#define EventWindowDidUpdateCollectionProperties 1149
#define EventWindowDidUpdateShadow 1150
#define EventWindowDidUpdateTitle 1151
#define EventWindowDidUpdateToolbar 1152
#define EventWindowDidZoom 1153
#define EventWindowFileDraggingEntered 1154
#define EventWindowFileDraggingExited 1155
#define EventWindowFileDraggingPerformed 1156
#define EventWindowHide 1157
#define EventWindowMaximise 1158
#define EventWindowUnMaximise 1159
#define EventWindowMinimise 1160
#define EventWindowUnMinimise 1161
#define EventWindowShouldClose 1162
#define EventWindowShow 1163
#define EventWindowWillBecomeKey 1164
#define EventWindowWillBecomeMain 1165
#define EventWindowWillBeginSheet 1166
#define EventWindowWillChangeOrderingMode 1167
#define EventWindowWillClose 1168
#define EventWindowWillDeminiaturize 1169
#define EventWindowWillEnterFullScreen 1170
#define EventWindowWillEnterVersionBrowser 1171
#define EventWindowWillExitFullScreen 1172
#define EventWindowWillExitVersionBrowser 1173
#define EventWindowWillFocus 1174
#define EventWindowWillMiniaturize 1175
#define EventWindowWillMove 1176
#define EventWindowWillOrderOffScreen 1177
#define EventWindowWillOrderOnScreen 1178
#define EventWindowWillResignMain 1179
#define EventWindowWillResize 1180
#define EventWindowWillUnfocus 1181
#define EventWindowWillUpdate 1182
#define EventWindowWillUpdateAlpha 1183
#define EventWindowWillUpdateCollectionBehavior 1184
#define EventWindowWillUpdateCollectionProperties 1185
#define EventWindowWillUpdateShadow 1186
#define EventWindowWillUpdateTitle 1187
#define EventWindowWillUpdateToolbar 1188
#define EventWindowWillUpdateVisibility 1189
#define EventWindowWillUseStandardFrame 1190
#define EventWindowZoomIn 1191
#define EventWindowZoomOut 1192
#define EventWindowZoomReset 1193
#define EventApplicationDidBecomeActive 1061
#define EventApplicationDidChangeBackingProperties 1062
#define EventApplicationDidChangeEffectiveAppearance 1063
#define EventApplicationDidChangeIcon 1064
#define EventApplicationDidChangeOcclusionState 1065
#define EventApplicationDidChangeScreenParameters 1066
#define EventApplicationDidChangeStatusBarFrame 1067
#define EventApplicationDidChangeStatusBarOrientation 1068
#define EventApplicationDidChangeTheme 1069
#define EventApplicationDidFinishLaunching 1070
#define EventApplicationDidHide 1071
#define EventApplicationDidResignActive 1072
#define EventApplicationDidUnhide 1073
#define EventApplicationDidUpdate 1074
#define EventApplicationShouldHandleReopen 1075
#define EventApplicationWillBecomeActive 1076
#define EventApplicationWillFinishLaunching 1077
#define EventApplicationWillHide 1078
#define EventApplicationWillResignActive 1079
#define EventApplicationWillTerminate 1080
#define EventApplicationWillUnhide 1081
#define EventApplicationWillUpdate 1082
#define EventMenuDidAddItem 1083
#define EventMenuDidBeginTracking 1084
#define EventMenuDidClose 1085
#define EventMenuDidDisplayItem 1086
#define EventMenuDidEndTracking 1087
#define EventMenuDidHighlightItem 1088
#define EventMenuDidOpen 1089
#define EventMenuDidPopUp 1090
#define EventMenuDidRemoveItem 1091
#define EventMenuDidSendAction 1092
#define EventMenuDidSendActionToItem 1093
#define EventMenuDidUpdate 1094
#define EventMenuWillAddItem 1095
#define EventMenuWillBeginTracking 1096
#define EventMenuWillDisplayItem 1097
#define EventMenuWillEndTracking 1098
#define EventMenuWillHighlightItem 1099
#define EventMenuWillOpen 1100
#define EventMenuWillPopUp 1101
#define EventMenuWillRemoveItem 1102
#define EventMenuWillSendAction 1103
#define EventMenuWillSendActionToItem 1104
#define EventMenuWillUpdate 1105
#define EventWebViewDidCommitNavigation 1106
#define EventWebViewDidFinishNavigation 1107
#define EventWebViewDidReceiveServerRedirectForProvisionalNavigation 1108
#define EventWebViewDidStartProvisionalNavigation 1109
#define EventWindowDidBecomeKey 1110
#define EventWindowDidBecomeMain 1111
#define EventWindowDidBeginSheet 1112
#define EventWindowDidChangeAlpha 1113
#define EventWindowDidChangeBackingLocation 1114
#define EventWindowDidChangeBackingProperties 1115
#define EventWindowDidChangeCollectionBehavior 1116
#define EventWindowDidChangeEffectiveAppearance 1117
#define EventWindowDidChangeOcclusionState 1118
#define EventWindowDidChangeOrderingMode 1119
#define EventWindowDidChangeScreen 1120
#define EventWindowDidChangeScreenParameters 1121
#define EventWindowDidChangeScreenProfile 1122
#define EventWindowDidChangeScreenSpace 1123
#define EventWindowDidChangeScreenSpaceProperties 1124
#define EventWindowDidChangeSharingType 1125
#define EventWindowDidChangeSpace 1126
#define EventWindowDidChangeSpaceOrderingMode 1127
#define EventWindowDidChangeTitle 1128
#define EventWindowDidChangeToolbar 1129
#define EventWindowDidDeminiaturize 1130
#define EventWindowDidEndSheet 1131
#define EventWindowDidEnterFullScreen 1132
#define EventWindowDidEnterVersionBrowser 1133
#define EventWindowDidExitFullScreen 1134
#define EventWindowDidExitVersionBrowser 1135
#define EventWindowDidExpose 1136
#define EventWindowDidFocus 1137
#define EventWindowDidMiniaturize 1138
#define EventWindowDidMove 1139
#define EventWindowDidOrderOffScreen 1140
#define EventWindowDidOrderOnScreen 1141
#define EventWindowDidResignKey 1142
#define EventWindowDidResignMain 1143
#define EventWindowDidResize 1144
#define EventWindowDidUpdate 1145
#define EventWindowDidUpdateAlpha 1146
#define EventWindowDidUpdateCollectionBehavior 1147
#define EventWindowDidUpdateCollectionProperties 1148
#define EventWindowDidUpdateShadow 1149
#define EventWindowDidUpdateTitle 1150
#define EventWindowDidUpdateToolbar 1151
#define EventWindowDidZoom 1152
#define EventWindowFileDraggingEntered 1153
#define EventWindowFileDraggingExited 1154
#define EventWindowFileDraggingPerformed 1155
#define EventWindowHide 1156
#define EventWindowMaximise 1157
#define EventWindowUnMaximise 1158
#define EventWindowMinimise 1159
#define EventWindowUnMinimise 1160
#define EventWindowShouldClose 1161
#define EventWindowShow 1162
#define EventWindowWillBecomeKey 1163
#define EventWindowWillBecomeMain 1164
#define EventWindowWillBeginSheet 1165
#define EventWindowWillChangeOrderingMode 1166
#define EventWindowWillClose 1167
#define EventWindowWillDeminiaturize 1168
#define EventWindowWillEnterFullScreen 1169
#define EventWindowWillEnterVersionBrowser 1170
#define EventWindowWillExitFullScreen 1171
#define EventWindowWillExitVersionBrowser 1172
#define EventWindowWillFocus 1173
#define EventWindowWillMiniaturize 1174
#define EventWindowWillMove 1175
#define EventWindowWillOrderOffScreen 1176
#define EventWindowWillOrderOnScreen 1177
#define EventWindowWillResignMain 1178
#define EventWindowWillResize 1179
#define EventWindowWillUnfocus 1180
#define EventWindowWillUpdate 1181
#define EventWindowWillUpdateAlpha 1182
#define EventWindowWillUpdateCollectionBehavior 1183
#define EventWindowWillUpdateCollectionProperties 1184
#define EventWindowWillUpdateShadow 1185
#define EventWindowWillUpdateTitle 1186
#define EventWindowWillUpdateToolbar 1187
#define EventWindowWillUpdateVisibility 1188
#define EventWindowWillUseStandardFrame 1189
#define EventWindowZoomIn 1190
#define EventWindowZoomOut 1191
#define EventWindowZoomReset 1192
#define MAX_EVENTS 1194
#define MAX_EVENTS 1193
#endif

View file

@ -6,30 +6,30 @@
extern void processApplicationEvent(unsigned int, void* data);
extern void processWindowEvent(unsigned int, unsigned int);
#define EventApplicationDidBecomeActive 1238
#define EventApplicationDidEnterBackground 1239
#define EventApplicationDidFinishLaunching 1240
#define EventApplicationDidReceiveMemoryWarning 1241
#define EventApplicationWillEnterForeground 1242
#define EventApplicationWillResignActive 1243
#define EventApplicationWillTerminate 1244
#define EventWindowDidLoad 1245
#define EventWindowWillAppear 1246
#define EventWindowDidAppear 1247
#define EventWindowWillDisappear 1248
#define EventWindowDidDisappear 1249
#define EventWindowSafeAreaInsetsChanged 1250
#define EventWindowOrientationChanged 1251
#define EventWindowTouchBegan 1252
#define EventWindowTouchMoved 1253
#define EventWindowTouchEnded 1254
#define EventWindowTouchCancelled 1255
#define EventWebViewDidStartNavigation 1256
#define EventWebViewDidFinishNavigation 1257
#define EventWebViewDidFailNavigation 1258
#define EventWebViewDecidePolicyForNavigationAction 1259
#define EventApplicationDidBecomeActive 1237
#define EventApplicationDidEnterBackground 1238
#define EventApplicationDidFinishLaunching 1239
#define EventApplicationDidReceiveMemoryWarning 1240
#define EventApplicationWillEnterForeground 1241
#define EventApplicationWillResignActive 1242
#define EventApplicationWillTerminate 1243
#define EventWindowDidLoad 1244
#define EventWindowWillAppear 1245
#define EventWindowDidAppear 1246
#define EventWindowWillDisappear 1247
#define EventWindowDidDisappear 1248
#define EventWindowSafeAreaInsetsChanged 1249
#define EventWindowOrientationChanged 1250
#define EventWindowTouchBegan 1251
#define EventWindowTouchMoved 1252
#define EventWindowTouchEnded 1253
#define EventWindowTouchCancelled 1254
#define EventWebViewDidStartNavigation 1255
#define EventWebViewDidFinishNavigation 1256
#define EventWebViewDidFailNavigation 1257
#define EventWebViewDecidePolicyForNavigationAction 1258
#define MAX_EVENTS 1260
#define MAX_EVENTS 1259
#endif

View file

@ -6,19 +6,19 @@
extern void processApplicationEvent(unsigned int, void* data);
extern void processWindowEvent(unsigned int, unsigned int);
#define EventApplicationStartup 1051
#define EventSystemThemeChanged 1052
#define EventWindowDeleteEvent 1053
#define EventWindowDidMove 1054
#define EventWindowDidResize 1055
#define EventWindowFocusIn 1056
#define EventWindowFocusOut 1057
#define EventWindowLoadStarted 1058
#define EventWindowLoadRedirected 1059
#define EventWindowLoadCommitted 1060
#define EventWindowLoadFinished 1061
#define EventApplicationStartup 1050
#define EventSystemThemeChanged 1051
#define EventWindowDeleteEvent 1052
#define EventWindowDidMove 1053
#define EventWindowDidResize 1054
#define EventWindowFocusIn 1055
#define EventWindowFocusOut 1056
#define EventWindowLoadStarted 1057
#define EventWindowLoadRedirected 1058
#define EventWindowLoadCommitted 1059
#define EventWindowLoadFinished 1060
#define MAX_EVENTS 1062
#define MAX_EVENTS 1061
#endif

View file

@ -6,218 +6,217 @@ func IsKnownEvent(name string) bool {
}
var knownEvents = map[string]struct{}{
"common:ApplicationOpenedWithFile": {},
"common:ApplicationStarted": {},
"common:ApplicationLaunchedWithUrl": {},
"common:ThemeChanged": {},
"common:WindowClosing": {},
"common:WindowDidMove": {},
"common:WindowDidResize": {},
"common:WindowDPIChanged": {},
"common:WindowFilesDropped": {},
"common:WindowFocus": {},
"common:WindowFullscreen": {},
"common:WindowHide": {},
"common:WindowLostFocus": {},
"common:WindowMaximise": {},
"common:WindowMinimise": {},
"common:WindowToggleFrameless": {},
"common:WindowRestore": {},
"common:WindowRuntimeReady": {},
"common:WindowShow": {},
"common:WindowUnFullscreen": {},
"common:WindowUnMaximise": {},
"common:WindowUnMinimise": {},
"common:WindowZoom": {},
"common:WindowZoomIn": {},
"common:WindowZoomOut": {},
"common:WindowZoomReset": {},
"common:WindowDropZoneFilesDropped": {},
"linux:ApplicationStartup": {},
"linux:SystemThemeChanged": {},
"linux:WindowDeleteEvent": {},
"linux:WindowDidMove": {},
"linux:WindowDidResize": {},
"linux:WindowFocusIn": {},
"linux:WindowFocusOut": {},
"linux:WindowLoadStarted": {},
"linux:WindowLoadRedirected": {},
"linux:WindowLoadCommitted": {},
"linux:WindowLoadFinished": {},
"mac:ApplicationDidBecomeActive": {},
"mac:ApplicationDidChangeBackingProperties": {},
"mac:ApplicationDidChangeEffectiveAppearance": {},
"mac:ApplicationDidChangeIcon": {},
"mac:ApplicationDidChangeOcclusionState": {},
"mac:ApplicationDidChangeScreenParameters": {},
"mac:ApplicationDidChangeStatusBarFrame": {},
"mac:ApplicationDidChangeStatusBarOrientation": {},
"mac:ApplicationDidChangeTheme": {},
"mac:ApplicationDidFinishLaunching": {},
"mac:ApplicationDidHide": {},
"mac:ApplicationDidResignActive": {},
"mac:ApplicationDidUnhide": {},
"mac:ApplicationDidUpdate": {},
"mac:ApplicationShouldHandleReopen": {},
"mac:ApplicationWillBecomeActive": {},
"mac:ApplicationWillFinishLaunching": {},
"mac:ApplicationWillHide": {},
"mac:ApplicationWillResignActive": {},
"mac:ApplicationWillTerminate": {},
"mac:ApplicationWillUnhide": {},
"mac:ApplicationWillUpdate": {},
"mac:MenuDidAddItem": {},
"mac:MenuDidBeginTracking": {},
"mac:MenuDidClose": {},
"mac:MenuDidDisplayItem": {},
"mac:MenuDidEndTracking": {},
"mac:MenuDidHighlightItem": {},
"mac:MenuDidOpen": {},
"mac:MenuDidPopUp": {},
"mac:MenuDidRemoveItem": {},
"mac:MenuDidSendAction": {},
"mac:MenuDidSendActionToItem": {},
"mac:MenuDidUpdate": {},
"mac:MenuWillAddItem": {},
"mac:MenuWillBeginTracking": {},
"mac:MenuWillDisplayItem": {},
"mac:MenuWillEndTracking": {},
"mac:MenuWillHighlightItem": {},
"mac:MenuWillOpen": {},
"mac:MenuWillPopUp": {},
"mac:MenuWillRemoveItem": {},
"mac:MenuWillSendAction": {},
"mac:MenuWillSendActionToItem": {},
"mac:MenuWillUpdate": {},
"mac:WebViewDidCommitNavigation": {},
"mac:WebViewDidFinishNavigation": {},
"common:ApplicationOpenedWithFile": {},
"common:ApplicationStarted": {},
"common:ApplicationLaunchedWithUrl": {},
"common:ThemeChanged": {},
"common:WindowClosing": {},
"common:WindowDidMove": {},
"common:WindowDidResize": {},
"common:WindowDPIChanged": {},
"common:WindowFilesDropped": {},
"common:WindowFocus": {},
"common:WindowFullscreen": {},
"common:WindowHide": {},
"common:WindowLostFocus": {},
"common:WindowMaximise": {},
"common:WindowMinimise": {},
"common:WindowToggleFrameless": {},
"common:WindowRestore": {},
"common:WindowRuntimeReady": {},
"common:WindowShow": {},
"common:WindowUnFullscreen": {},
"common:WindowUnMaximise": {},
"common:WindowUnMinimise": {},
"common:WindowZoom": {},
"common:WindowZoomIn": {},
"common:WindowZoomOut": {},
"common:WindowZoomReset": {},
"linux:ApplicationStartup": {},
"linux:SystemThemeChanged": {},
"linux:WindowDeleteEvent": {},
"linux:WindowDidMove": {},
"linux:WindowDidResize": {},
"linux:WindowFocusIn": {},
"linux:WindowFocusOut": {},
"linux:WindowLoadStarted": {},
"linux:WindowLoadRedirected": {},
"linux:WindowLoadCommitted": {},
"linux:WindowLoadFinished": {},
"mac:ApplicationDidBecomeActive": {},
"mac:ApplicationDidChangeBackingProperties": {},
"mac:ApplicationDidChangeEffectiveAppearance": {},
"mac:ApplicationDidChangeIcon": {},
"mac:ApplicationDidChangeOcclusionState": {},
"mac:ApplicationDidChangeScreenParameters": {},
"mac:ApplicationDidChangeStatusBarFrame": {},
"mac:ApplicationDidChangeStatusBarOrientation": {},
"mac:ApplicationDidChangeTheme": {},
"mac:ApplicationDidFinishLaunching": {},
"mac:ApplicationDidHide": {},
"mac:ApplicationDidResignActive": {},
"mac:ApplicationDidUnhide": {},
"mac:ApplicationDidUpdate": {},
"mac:ApplicationShouldHandleReopen": {},
"mac:ApplicationWillBecomeActive": {},
"mac:ApplicationWillFinishLaunching": {},
"mac:ApplicationWillHide": {},
"mac:ApplicationWillResignActive": {},
"mac:ApplicationWillTerminate": {},
"mac:ApplicationWillUnhide": {},
"mac:ApplicationWillUpdate": {},
"mac:MenuDidAddItem": {},
"mac:MenuDidBeginTracking": {},
"mac:MenuDidClose": {},
"mac:MenuDidDisplayItem": {},
"mac:MenuDidEndTracking": {},
"mac:MenuDidHighlightItem": {},
"mac:MenuDidOpen": {},
"mac:MenuDidPopUp": {},
"mac:MenuDidRemoveItem": {},
"mac:MenuDidSendAction": {},
"mac:MenuDidSendActionToItem": {},
"mac:MenuDidUpdate": {},
"mac:MenuWillAddItem": {},
"mac:MenuWillBeginTracking": {},
"mac:MenuWillDisplayItem": {},
"mac:MenuWillEndTracking": {},
"mac:MenuWillHighlightItem": {},
"mac:MenuWillOpen": {},
"mac:MenuWillPopUp": {},
"mac:MenuWillRemoveItem": {},
"mac:MenuWillSendAction": {},
"mac:MenuWillSendActionToItem": {},
"mac:MenuWillUpdate": {},
"mac:WebViewDidCommitNavigation": {},
"mac:WebViewDidFinishNavigation": {},
"mac:WebViewDidReceiveServerRedirectForProvisionalNavigation": {},
"mac:WebViewDidStartProvisionalNavigation": {},
"mac:WindowDidBecomeKey": {},
"mac:WindowDidBecomeMain": {},
"mac:WindowDidBeginSheet": {},
"mac:WindowDidChangeAlpha": {},
"mac:WindowDidChangeBackingLocation": {},
"mac:WindowDidChangeBackingProperties": {},
"mac:WindowDidChangeCollectionBehavior": {},
"mac:WindowDidChangeEffectiveAppearance": {},
"mac:WindowDidChangeOcclusionState": {},
"mac:WindowDidChangeOrderingMode": {},
"mac:WindowDidChangeScreen": {},
"mac:WindowDidChangeScreenParameters": {},
"mac:WindowDidChangeScreenProfile": {},
"mac:WindowDidChangeScreenSpace": {},
"mac:WindowDidChangeScreenSpaceProperties": {},
"mac:WindowDidChangeSharingType": {},
"mac:WindowDidChangeSpace": {},
"mac:WindowDidChangeSpaceOrderingMode": {},
"mac:WindowDidChangeTitle": {},
"mac:WindowDidChangeToolbar": {},
"mac:WindowDidDeminiaturize": {},
"mac:WindowDidEndSheet": {},
"mac:WindowDidEnterFullScreen": {},
"mac:WindowDidEnterVersionBrowser": {},
"mac:WindowDidExitFullScreen": {},
"mac:WindowDidExitVersionBrowser": {},
"mac:WindowDidExpose": {},
"mac:WindowDidFocus": {},
"mac:WindowDidMiniaturize": {},
"mac:WindowDidMove": {},
"mac:WindowDidOrderOffScreen": {},
"mac:WindowDidOrderOnScreen": {},
"mac:WindowDidResignKey": {},
"mac:WindowDidResignMain": {},
"mac:WindowDidResize": {},
"mac:WindowDidUpdate": {},
"mac:WindowDidUpdateAlpha": {},
"mac:WindowDidUpdateCollectionBehavior": {},
"mac:WindowDidUpdateCollectionProperties": {},
"mac:WindowDidUpdateShadow": {},
"mac:WindowDidUpdateTitle": {},
"mac:WindowDidUpdateToolbar": {},
"mac:WindowDidZoom": {},
"mac:WindowFileDraggingEntered": {},
"mac:WindowFileDraggingExited": {},
"mac:WindowFileDraggingPerformed": {},
"mac:WindowHide": {},
"mac:WindowMaximise": {},
"mac:WindowUnMaximise": {},
"mac:WindowMinimise": {},
"mac:WindowUnMinimise": {},
"mac:WindowShouldClose": {},
"mac:WindowShow": {},
"mac:WindowWillBecomeKey": {},
"mac:WindowWillBecomeMain": {},
"mac:WindowWillBeginSheet": {},
"mac:WindowWillChangeOrderingMode": {},
"mac:WindowWillClose": {},
"mac:WindowWillDeminiaturize": {},
"mac:WindowWillEnterFullScreen": {},
"mac:WindowWillEnterVersionBrowser": {},
"mac:WindowWillExitFullScreen": {},
"mac:WindowWillExitVersionBrowser": {},
"mac:WindowWillFocus": {},
"mac:WindowWillMiniaturize": {},
"mac:WindowWillMove": {},
"mac:WindowWillOrderOffScreen": {},
"mac:WindowWillOrderOnScreen": {},
"mac:WindowWillResignMain": {},
"mac:WindowWillResize": {},
"mac:WindowWillUnfocus": {},
"mac:WindowWillUpdate": {},
"mac:WindowWillUpdateAlpha": {},
"mac:WindowWillUpdateCollectionBehavior": {},
"mac:WindowWillUpdateCollectionProperties": {},
"mac:WindowWillUpdateShadow": {},
"mac:WindowWillUpdateTitle": {},
"mac:WindowWillUpdateToolbar": {},
"mac:WindowWillUpdateVisibility": {},
"mac:WindowWillUseStandardFrame": {},
"mac:WindowZoomIn": {},
"mac:WindowZoomOut": {},
"mac:WindowZoomReset": {},
"windows:APMPowerSettingChange": {},
"windows:APMPowerStatusChange": {},
"windows:APMResumeAutomatic": {},
"windows:APMResumeSuspend": {},
"windows:APMSuspend": {},
"windows:ApplicationStarted": {},
"windows:SystemThemeChanged": {},
"windows:WebViewNavigationCompleted": {},
"windows:WindowActive": {},
"windows:WindowBackgroundErase": {},
"windows:WindowClickActive": {},
"windows:WindowClosing": {},
"windows:WindowDidMove": {},
"windows:WindowDidResize": {},
"windows:WindowDPIChanged": {},
"windows:WindowDragDrop": {},
"windows:WindowDragEnter": {},
"windows:WindowDragLeave": {},
"windows:WindowDragOver": {},
"windows:WindowEndMove": {},
"windows:WindowEndResize": {},
"windows:WindowFullscreen": {},
"windows:WindowHide": {},
"windows:WindowInactive": {},
"windows:WindowKeyDown": {},
"windows:WindowKeyUp": {},
"windows:WindowKillFocus": {},
"windows:WindowNonClientHit": {},
"windows:WindowNonClientMouseDown": {},
"windows:WindowNonClientMouseLeave": {},
"windows:WindowNonClientMouseMove": {},
"windows:WindowNonClientMouseUp": {},
"windows:WindowPaint": {},
"windows:WindowRestore": {},
"windows:WindowSetFocus": {},
"windows:WindowShow": {},
"windows:WindowStartMove": {},
"windows:WindowStartResize": {},
"windows:WindowUnFullscreen": {},
"windows:WindowZOrderChanged": {},
"windows:WindowMinimise": {},
"windows:WindowUnMinimise": {},
"windows:WindowMaximise": {},
"windows:WindowUnMaximise": {},
"mac:WebViewDidStartProvisionalNavigation": {},
"mac:WindowDidBecomeKey": {},
"mac:WindowDidBecomeMain": {},
"mac:WindowDidBeginSheet": {},
"mac:WindowDidChangeAlpha": {},
"mac:WindowDidChangeBackingLocation": {},
"mac:WindowDidChangeBackingProperties": {},
"mac:WindowDidChangeCollectionBehavior": {},
"mac:WindowDidChangeEffectiveAppearance": {},
"mac:WindowDidChangeOcclusionState": {},
"mac:WindowDidChangeOrderingMode": {},
"mac:WindowDidChangeScreen": {},
"mac:WindowDidChangeScreenParameters": {},
"mac:WindowDidChangeScreenProfile": {},
"mac:WindowDidChangeScreenSpace": {},
"mac:WindowDidChangeScreenSpaceProperties": {},
"mac:WindowDidChangeSharingType": {},
"mac:WindowDidChangeSpace": {},
"mac:WindowDidChangeSpaceOrderingMode": {},
"mac:WindowDidChangeTitle": {},
"mac:WindowDidChangeToolbar": {},
"mac:WindowDidDeminiaturize": {},
"mac:WindowDidEndSheet": {},
"mac:WindowDidEnterFullScreen": {},
"mac:WindowDidEnterVersionBrowser": {},
"mac:WindowDidExitFullScreen": {},
"mac:WindowDidExitVersionBrowser": {},
"mac:WindowDidExpose": {},
"mac:WindowDidFocus": {},
"mac:WindowDidMiniaturize": {},
"mac:WindowDidMove": {},
"mac:WindowDidOrderOffScreen": {},
"mac:WindowDidOrderOnScreen": {},
"mac:WindowDidResignKey": {},
"mac:WindowDidResignMain": {},
"mac:WindowDidResize": {},
"mac:WindowDidUpdate": {},
"mac:WindowDidUpdateAlpha": {},
"mac:WindowDidUpdateCollectionBehavior": {},
"mac:WindowDidUpdateCollectionProperties": {},
"mac:WindowDidUpdateShadow": {},
"mac:WindowDidUpdateTitle": {},
"mac:WindowDidUpdateToolbar": {},
"mac:WindowDidZoom": {},
"mac:WindowFileDraggingEntered": {},
"mac:WindowFileDraggingExited": {},
"mac:WindowFileDraggingPerformed": {},
"mac:WindowHide": {},
"mac:WindowMaximise": {},
"mac:WindowUnMaximise": {},
"mac:WindowMinimise": {},
"mac:WindowUnMinimise": {},
"mac:WindowShouldClose": {},
"mac:WindowShow": {},
"mac:WindowWillBecomeKey": {},
"mac:WindowWillBecomeMain": {},
"mac:WindowWillBeginSheet": {},
"mac:WindowWillChangeOrderingMode": {},
"mac:WindowWillClose": {},
"mac:WindowWillDeminiaturize": {},
"mac:WindowWillEnterFullScreen": {},
"mac:WindowWillEnterVersionBrowser": {},
"mac:WindowWillExitFullScreen": {},
"mac:WindowWillExitVersionBrowser": {},
"mac:WindowWillFocus": {},
"mac:WindowWillMiniaturize": {},
"mac:WindowWillMove": {},
"mac:WindowWillOrderOffScreen": {},
"mac:WindowWillOrderOnScreen": {},
"mac:WindowWillResignMain": {},
"mac:WindowWillResize": {},
"mac:WindowWillUnfocus": {},
"mac:WindowWillUpdate": {},
"mac:WindowWillUpdateAlpha": {},
"mac:WindowWillUpdateCollectionBehavior": {},
"mac:WindowWillUpdateCollectionProperties": {},
"mac:WindowWillUpdateShadow": {},
"mac:WindowWillUpdateTitle": {},
"mac:WindowWillUpdateToolbar": {},
"mac:WindowWillUpdateVisibility": {},
"mac:WindowWillUseStandardFrame": {},
"mac:WindowZoomIn": {},
"mac:WindowZoomOut": {},
"mac:WindowZoomReset": {},
"windows:APMPowerSettingChange": {},
"windows:APMPowerStatusChange": {},
"windows:APMResumeAutomatic": {},
"windows:APMResumeSuspend": {},
"windows:APMSuspend": {},
"windows:ApplicationStarted": {},
"windows:SystemThemeChanged": {},
"windows:WebViewNavigationCompleted": {},
"windows:WindowActive": {},
"windows:WindowBackgroundErase": {},
"windows:WindowClickActive": {},
"windows:WindowClosing": {},
"windows:WindowDidMove": {},
"windows:WindowDidResize": {},
"windows:WindowDPIChanged": {},
"windows:WindowDragDrop": {},
"windows:WindowDragEnter": {},
"windows:WindowDragLeave": {},
"windows:WindowDragOver": {},
"windows:WindowEndMove": {},
"windows:WindowEndResize": {},
"windows:WindowFullscreen": {},
"windows:WindowHide": {},
"windows:WindowInactive": {},
"windows:WindowKeyDown": {},
"windows:WindowKeyUp": {},
"windows:WindowKillFocus": {},
"windows:WindowNonClientHit": {},
"windows:WindowNonClientMouseDown": {},
"windows:WindowNonClientMouseLeave": {},
"windows:WindowNonClientMouseMove": {},
"windows:WindowNonClientMouseUp": {},
"windows:WindowPaint": {},
"windows:WindowRestore": {},
"windows:WindowSetFocus": {},
"windows:WindowShow": {},
"windows:WindowStartMove": {},
"windows:WindowStartResize": {},
"windows:WindowUnFullscreen": {},
"windows:WindowZOrderChanged": {},
"windows:WindowMinimise": {},
"windows:WindowUnMinimise": {},
"windows:WindowMaximise": {},
"windows:WindowUnMaximise": {},
}