Skip to content

Conversation

@APshenkin
Copy link
Contributor

@APshenkin APshenkin commented Feb 11, 2024

Description

Native drag and drop with full file path for macOS. #1090
Also changed few things:
– changed DragAndDrop to be a link in app options
– renamed DragAndDrop.Disable to DragAndDrop.DisableWebViewDrop to be more clear about what we are disabling
EventsOff import was missing draganddrop.js
– Fixed setting class property when moving hover to and away target element

1 UX nuance that I found: if you have Native drag and drop disabled and you try to drop file somewhere, where it's not allowed in UI, you can see animation where element "return back". When you enable native drag and drop you loose this effect. As well that some additional icons are not displayed (e.g. if you drop to input field). I think this is because we set drop and dragover listeners for entire window in draganddrop.js here

window.addEventListener('dragover', onDragOver);

This is minor thing and I think we can go with it.

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration using wails doctor.

  • Windows
  • macOS
  • Linux

Test Configuration

# System
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
| OS           | MacOS                                                                                                                       |
| Version      | 14.3                                                                                                                        |
| ID           | 23D56                                                                                                                       |
| Go Version   | go1.21.3                                                                                                                    |
| Platform     | darwin                                                                                                                      |
| Architecture | arm64                                                                                                                       |
| CPU          | Apple M1 Max                                                                                                                |
| GPU          | Chipset Model: Apple M1 Max Type: GPU Bus: Built-In Total Number of Cores: 32 Vendor: Apple (0x106b) Metal Support: Metal 3 |
| Memory       | 32GB                                                                                                                        |
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Checklist:

  • I have updated website/src/pages/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features
    • Introduced Drag & Drop support for macOS, enhancing file handling and interaction within the app.
  • Documentation
    • Updated documentation to reflect the new Drag & Drop capabilities across all platforms and the modifications to the DragAndDrop option structure.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2024

Important

Auto Review Skipped

Auto reviews are disabled on base/target branches other than the default branch. Please add the base/target branch pattern to the list of additional branches to be reviewed in the settings.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository.

To trigger a single review, invoke the @coderabbitai review command.

Walkthrough

This update introduces enhanced drag-and-drop functionality for macOS within the Wails framework, adding support for enabling or disabling drag-and-drop in both the general application context and specifically within the WebView component. It also introduces a custom WailsWebView class to manage these interactions more effectively.

Changes

File Path Change Summary
v2/internal/frontend/desktop/darwin/.../Application.h, Application.m Updated Create function in WailsContext to include enableDragAndDrop and disableWebViewDragAndDrop parameters.
v2/internal/frontend/desktop/darwin/.../WailsContext.h, WailsContext.m Imported WailsWebView.h, changed WKWebView to WailsWebView, and updated method signatures to include drag-and-drop parameters.
v2/internal/frontend/desktop/darwin/.../WailsWebView.h, WailsWebView.m Introduced and implemented WailsWebView class for customizable drag-and-drop behavior.
v2/internal/frontend/desktop/darwin/frontend.go, window.go Adjusted to handle new drag-and-drop options. Similar changes applied to Linux window.go.
v2/internal/frontend/runtime/... Refactored drag-and-drop and other runtime functionalities.
v2/pkg/options/options.go Modified DragAndDrop field in App struct and updated related logic.
website/docs/... Updated documentation to reflect changes in drag-and-drop functionality and options.

Related issues

  • [FEAT]: Native drap and drop for file and folder with complete filepath #1090: The enhancements in drag-and-drop support for macOS could indirectly address objectives related to improving drag-and-drop functionality across platforms, making it relevant to link to this PR. The PR's focus on cross-platform capabilities, including the customization of drag-and-drop behavior, aligns with the issue's objectives to enhance file and folder drag-and-drop functionality and reduce dependency on specific browsers or technologies.

Poem

In the field of green, under the bright light's sheen,
Chiefs and 49ers, in a showdown so keen.
Amidst the clash, a rabbit hops with glee,
Celebrating code that sets the drag-and-drop free.
🏈 Over screens, the passes fly, like footballs in the air,
In the spirit of the game, we code with flair.
Let's cheer on the teams, as we code away,
For in the heart of the game, our spirits sway.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share

Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit-tests for this file.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit tests for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository from git and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit tests.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger a review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • The JSON schema for the configuration file is available here.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/coderabbit-overrides.v2.json

CodeRabbit Discord Community

Join our Discord Community to get help, request features, and share feedback.


self.webview = [WKWebView alloc];

self.webview = [WailsWebView alloc];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The important change is here. We use our extended implementation of WKWebkit here

@leaanthony
Copy link
Member

Thanks for this @APshenkin 🙏 I'd be keen to hear @pavelbinar and @lyimmi's thoughts. Awesome work man!

@APshenkin
Copy link
Contributor Author

@coderabbitai review

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review Status

Actionable comments generated: 1

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between f9df564 and 32bd295.
Files selected for processing (15)
  • v2/internal/frontend/desktop/darwin/Application.h (1 hunks)
  • v2/internal/frontend/desktop/darwin/Application.m (2 hunks)
  • v2/internal/frontend/desktop/darwin/WailsContext.h (3 hunks)
  • v2/internal/frontend/desktop/darwin/WailsContext.m (24 hunks)
  • v2/internal/frontend/desktop/darwin/WailsWebView.h (1 hunks)
  • v2/internal/frontend/desktop/darwin/WailsWebView.m (1 hunks)
  • v2/internal/frontend/desktop/darwin/frontend.go (1 hunks)
  • v2/internal/frontend/desktop/darwin/window.go (2 hunks)
  • v2/internal/frontend/desktop/linux/window.go (1 hunks)
  • v2/internal/frontend/runtime/desktop/draganddrop.js (5 hunks)
  • v2/internal/frontend/runtime/runtime_prod_desktop.js (1 hunks)
  • v2/pkg/options/options.go (3 hunks)
  • website/docs/reference/options.mdx (3 hunks)
  • website/docs/reference/runtime/draganddrop.mdx (1 hunks)
  • website/src/pages/changelog.mdx (1 hunks)
Files skipped from review due to trivial changes (1)
  • website/docs/reference/runtime/draganddrop.mdx
Additional comments: 24
v2/internal/frontend/desktop/darwin/WailsWebView.h (1)
  • 1-14: LGTM!
v2/internal/frontend/desktop/darwin/Application.h (1)
  • 20-20: LGTM!
v2/internal/frontend/desktop/darwin/WailsContext.h (3)
  • 13-13: LGTM!
  • 36-36: LGTM!
  • 68-68: LGTM!
v2/internal/frontend/desktop/darwin/WailsWebView.m (1)
  • 1-120: LGTM!
v2/internal/frontend/runtime/desktop/draganddrop.js (4)
  • 13-13: LGTM!
  • 81-81: LGTM!
  • 103-103: LGTM!
  • 125-126: LGTM!
v2/pkg/options/options.go (3)
  • 100-100: LGTM!
  • 156-158: LGTM!
  • 203-203: LGTM!
v2/internal/frontend/desktop/darwin/window.go (2)
  • 86-88: LGTM!
  • 123-123: LGTM!
v2/internal/frontend/desktop/darwin/frontend.go (1)
  • 367-371: LGTM!
v2/internal/frontend/runtime/runtime_prod_desktop.js (1)
  • 1-1: > 📝 NOTE

This review was outside the diff hunks, and no overlapping diff hunk was found. Original lines [104-105]

The conditional checks for appoptions.DragAndDrop being not nil before accessing its properties are correct and prevent potential null reference exceptions.

v2/internal/frontend/desktop/linux/window.go (1)
  • 104-105: The conditional checks for appoptions.DragAndDrop being not nil before accessing its properties are correct and prevent potential null reference exceptions.
v2/internal/frontend/desktop/darwin/Application.m (1)
  • 14-20: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [17-30]

The addition of enableDragAndDrop and disableWebViewDragAndDrop parameters to the Create function in WailsContext correctly extends its functionality to support configurable drag and drop behavior.

v2/internal/frontend/desktop/darwin/WailsContext.m (2)
  • 155-177: > 📝 NOTE

This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [139-264]

The CreateWindow method has been significantly modified to support drag and drop functionality. Ensure that the enableDragAndDrop and disableWebViewDragAndDrop parameters are correctly used to configure the WailsWebView instance. Also, verify that the WailsWebView initialization and configuration are correctly implemented.

  • 436-446: The method signature for webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler: has been updated. Confirm that this change aligns with the correct method signature as per the WebKit documentation and that the implementation correctly handles the open panel functionality.
website/docs/reference/options.mdx (2)
  • 67-72: The documentation correctly reflects the conversion of the DragAndDrop field to a pointer and the renaming of the Disable field to DisableWebViewDrop. Ensure that the example code and descriptions accurately represent the new structure and naming conventions.
  • 558-564: The documentation for the DisableWebViewDrop field is clear and accurately describes its purpose. Ensure that the default value and behavior are correctly documented and that the example code reflects the new naming convention.
website/src/pages/changelog.mdx (1)
  • 26-26: Ensure the changelog entry for macOS drag & drop support is consistent with the format and style of previous entries.

#import "WailsContext.h"
#import "WailsAlert.h"
#import "WailsMenu.h"
#import "WailsWebView.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing newline after the import statement for WailsWebView.h for consistency with other import statements.

#import "WailsWebView.h"
+ 

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
#import "WailsWebView.h"
#import "WailsWebView.h"

@pavelbinar
Copy link
Contributor

@APshenkin This is great 🎉
We will try to review it on Monday/Friday next week.

@lyimmi
Copy link
Contributor

lyimmi commented Feb 11, 2024

Nice work @APshenkin!

I tested it on linux it works as expected.

On windows the f.frontendOptions.DragAndDrop.Disable check should be renamed to the new name in v2/internal/frontend/desktop/windows/frontend.go:465. After I replaced the check, it works on windows too.

Thanks for catching my mistakes in the JS files!

On the issue of loosing some functionality when D&D is set to disabled/enabled, I was thinking about that before and yes the cause of it is that all drop events are hidden from the webview.

The same thing happens when the OnFileDrop is registered on the JS side, you cannot drop to any type of input anymore. I think we could solve this by firing custom drop, or changed events based on the positions, but I'm not sure if its a good idea. I don't know how it would behave with the different frontend frameworks and that we could make a robust enough solution that works with all of them.

When someone wants to have the original behavior as well, I think in that case they should handle the drop events manually to have a greater level of control over it. Maybe we should document this? @pavelbinar @leaanthony

@APshenkin
Copy link
Contributor Author

@lyimmi thank you for the hint!. For some reason my IDE didn't replace it for Windows. Fixed

@jakubpeleska
Copy link
Contributor

jakubpeleska commented Feb 12, 2024

Hi, guys. Thanks, @APshenkin and @lyimmi for a great job on the PR.

I work with @pavelbinar and today, we were able to test it on MacOS, and it works well.

The only issue I found is that the removal of the wails-drop-target-active class on dragleave doesn't work correctly if the target element has the size of the whole window. In that case, the class stays on the element even though dragleave was triggered. But that seems to be a general issue, not MacOS-specific.

@lyimmi
Copy link
Contributor

lyimmi commented Feb 12, 2024

@APshenkin, @jakubpeleska, @pavelbinar
It seems that its a Javascript "behavior"...

We could use the "counter method" for this.

Add a new flag in v2/internal/frontend/runtime/desktop/draganddrop.js:

const flags = {
    registered: false,
    defaultUseDropTarget: true,
    useDropTarget: true,
    prevElement: null,
    dragCount: 0, <-- this
};

Add a new listener:

function onDragEnter(e) {
    if (!window.wails.flags.enableWailsDragAndDrop) {
        return;
    }
    e.preventDefault();

    if (!flags.useDropTarget) {
        return;
    }

    flags.dragCount++;
}
...
window.addEventListener('dragenter', onDragEnter);
...
window.removeEventListener('dragenter', onDragEnter);

Zero the counter on drop:

function onDrop(e) {
    if (!window.wails.flags.enableWailsDragAndDrop) {
        return;
    }
    e.preventDefault();

    if (!flags.useDropTarget) {
        return;
    }

    flags.dragCount = 0;
...

Decrement the counter on leave and clear the class when the counter is zero:

function onDragLeave(e) {
    if (!window.wails.flags.enableWailsDragAndDrop) {
        return;
    }
    e.preventDefault();

    if (!flags.useDropTarget) {
        return;
    }
    flags.dragCount--;

    let targetElement = document.elementFromPoint(e.x, e.y);
    let cssDropValue = window.getComputedStyle(targetElement).getPropertyValue(window.wails.flags.cssDropProperty);
    if (cssDropValue) {
        cssDropValue = cssDropValue.trim();
    }
    if (flags.prevElement && (cssDropValue !== window.wails.flags.cssDropValue || flags.dragCount === 0))  {
        targetElement.classList.remove("wails-drop-target-active");
        flags.prevElement.classList.remove("wails-drop-target-active");
        // remove element as otherwise we will not update the class on the next dragover
        flags.prevElement = null;
    }
}

With this the class is reset correctly, I don't really like it but it works.

Screencast.from.2024-02-12.16.54.40.webm

@APshenkin
Copy link
Contributor Author

APshenkin commented Feb 14, 2024

@lyimmi @jakubpeleska

I tried @lyimmi solution with macOS and for some reasons it didn't work.

So I came up with other way to solve it. Seems it works (at least on macOS 😅) and do this without counter which I think was the reason (as if something will register several times, then counter drop will not work).

Also I covered the case when you have nested elements in your --wails-drop-target. Before it didn't clean it properly I think.

Code for this is a bit ugly, but I didn't come up with something better than checking coordinates...

Can you guys check that it works on your side too?

Screen.Recording.2024-02-14.at.02.19.38.mov

@jakubpeleska
Copy link
Contributor

jakubpeleska commented Feb 16, 2024

Hi everyone,
Thank you for the fixes. Overall, I believe the solution proposed by @APshenkin is superior. We have tested it and found it to work well; however, we identified one edge case where the behavior does not function as expected:
The removal of the wails-drop-target-active class on dragleave event does not work correctly if the target element is obscured by another window.
Please see the attached video for a demonstration of this issue.

Screen.Recording.2024-02-16.at.16.05.36.mov

@pavelbinar
Copy link
Contributor

Hi guys, I would suggest the following:

The primary goal of this PR is the low-level implementation of drag and drop - providing the URLs for the files and folders and registering the events for such actions.

The issues and implementation of the front-end are an addition. In our specific case, it is all we need, because we will still listen for the entire window (we do not need drop zones). Also, I can imagine other edge cases when you have multiple drop zones and you need to identify which one was used - this is not currently supported.

My point is:
Let's keep this lean and functional and leave the specific front-end implementation to each user (for now). This will allow us to ship this feature fast and at the same time not ship a half-baked, non-essential feature.

What do you think?

@lyimmi
Copy link
Contributor

lyimmi commented Feb 16, 2024

I'm ok with just the events without the runtime part.

Edit: I mean the drop target part. Windows still needs Javascript to catch the drop.

@leaanthony
Copy link
Member

Agree with @pavelbinar. Is there a way to separate the 2 concerns? Perhaps hooks? We could also add the ui part later as a flag or document it. Thoughts?

@lyimmi
Copy link
Contributor

lyimmi commented Feb 16, 2024

@leaanthony it can be separated relatively simply.

We should register an event listener on the window for the drop for windows, that only handles the path resolution by calling postMessageWithAdditionalObjects(). Remove the cssDropProperty & cssDropValue from the app options.

Simplified to something like this, it could be moved to the main.js file under other listeners. This should be enough for windows.

function onDrop(e) {
    if (!window.wails.flags.enableWailsDragAndDrop) {
        return;
    }
    e.preventDefault();

    if (window.chrome?.webview?.postMessageWithAdditionalObjects != null) {
        // process files
        let files = [];
        if (e.dataTransfer.items) {
            files = [...e.dataTransfer.items].map((item, i) => {
                if (item.kind === 'file') {
                    return item.getAsFile();
                }
            });
        } else {
            files = [...e.dataTransfer.files];
        }
        chrome.webview.postMessageWithAdditionalObjects(`file:drop:${e.x}:${e.y}`, files);
    }
}

window.addEventListener('drop', onDrop);

In addition I would start with window.wails.flags.enableWailsDragAndDrop = true; and disable it on dom ready if the functionality is not needed. Because a user can drop a file in before it's enabled by dom ready.

@APshenkin
Copy link
Contributor Author

@leaanthony @lyimmi

maybe then merge this PR to the branch and then someone can pick up separation of JS part?

I'm not too familiar with Windows JS magic 😅

@leaanthony leaanthony merged commit 318628d into wailsapp:feature/1090_native_drag_and_drop_for_file_and_folder Feb 18, 2024
@pavelbinar
Copy link
Contributor

🚀

@jakubpeleska jakubpeleska mentioned this pull request Apr 20, 2024
15 tasks
@jakubpeleska jakubpeleska mentioned this pull request May 28, 2024
12 tasks
leaanthony added a commit that referenced this pull request Jun 10, 2024
* Feature/1090 native drag and drop for file and folder (#3203)

* implement basic dnd for linux

* implemented windows

* progress changed linux handling and added coordinates to drop

* progress fix drop coordinates on windows

* progress remove log from windows

* progress move js

* update js after merge

* fix event listener registration

* fix segfault on non file drag

* remove logs, fix coordinates

* minor changes, simplify to drop only

* rename EnableWails -> EnableFileDrop

* add documentation (PR id missing)

* add PR id to changelog

* fix remove casting from malloc

* fix nil check for OnFileDrop's callback

* fix nil check for OnFileDrop skip event when nil

* add error message for nil callback in OnFileDrop

---------

Co-authored-by: lyimmi <lelvente.zambo@gmail.com>
Co-authored-by: Lea Anthony <lea.anthony@gmail.com>

* implement native drag and drop for macOS (#3250)

* implement native drag and drop for macOS

* update docs

* add to changelog

* update docs (macOS is supported)

* Fix windows DragAndDrop options

* Fix class unset on dragleave for full frame elements

* improve class unset (nested elements and borders case)

* Fix runtime drop target detection and CSS class assignment

* Edit changelog

* Fix drag-and-drop options in references

* Update v2/internal/frontend/desktop/darwin/WailsWebView.m

* Update v2/internal/frontend/desktop/darwin/WailsWebView.m

---------

Co-authored-by: Zámbó, Levente <levente.zambo@gmail.com>
Co-authored-by: lyimmi <lelvente.zambo@gmail.com>
Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Co-authored-by: Andrey Pshenkin <andrey.pshenkin@gmail.com>
Co-authored-by: Pavel Binar <pavel@beamtransfer.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants