Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[FEAT]: Native drap and drop for file and folder with complete filepath #1090

Closed
hinupurthakur opened this issue Jan 20, 2022 · 77 comments · Fixed by #3203 or #3595
Closed

[FEAT]: Native drap and drop for file and folder with complete filepath #1090

hinupurthakur opened this issue Jan 20, 2022 · 77 comments · Fixed by #3203 or #3595
Labels
Enhancement New feature or request TODO The issue is ready to be developed

Comments

@hinupurthakur
Copy link

Is your feature request related to a problem? Please describe.
Currently, on Wails v1.16.8 as for windows development it depends on IE11 (as Windows development use the mshtml library which is only compatible with IE11), and On IE11, the drag and drop is not supported for folder. Whereas with Chrome, we can have file or folder drag and drop functionality.

Describe the solution you'd like
Feature that can be suitable would be native drag and drop with absolute path of dropped file/folder and if the dependency to IE11 can be removed.

Describe alternatives you've considered
Currently, We have tried the following approaches but not complete solution was found:

  1. Use ngx-file-drop library for drag and drop, it worked fine for chrome but for IE11, only file drop was working fine and not folder. But was not providing absolute path of neither file not folder..
  2. HTML Drag and drop: Failed for both file and folder.
  3. Drag and drop on Input Type=File: Failed for both file and folder.
  4. Created our own directive even it failed for both file and folder.

Additional context
Screenshot from example Feature Request

@hinupurthakur hinupurthakur added the Enhancement New feature or request label Jan 20, 2022
@leaanthony
Copy link
Member

For Windows, the recommendation is to move to v2.

@hinupurthakur
Copy link
Author

@leaanthony On v2, Do we have the native drag and drop functionality.

@leaanthony
Copy link
Member

No, but HTML dnd should.

@hinupurthakur
Copy link
Author

No, but HTML dnd should.

Okay Let me check. I will update here with its status.

@hinupurthakur
Copy link
Author

@leaanthony It worked with wails v2 but We are not getting the full path of the drag and dropped file. Which make sense for the browser but the full path for the local machine should be accessible. Do we have any work around in wails?

@leaanthony
Copy link
Member

Thanks for replying. Not right now but happy to accept a PR if it works on all platforms. Why do you need the path?

@hinupurthakur
Copy link
Author

hinupurthakur commented Jan 24, 2022

Okay, I will be happy to contribute for this feature. Should I include the complete drag and drop functionality ( HTML implementation) with full path as Native drag and drop?

Also, I need the path as I want to create some chunks of the file at the location of where the file is located and that is to be done from the Backend (Go)

@misitebao
Copy link
Contributor

Okay, I will be happy to contribute for this feature. Should I include the complete drag and drop functionality ( HTML implementation) with full path as Native drag and drop?

Also, I need the path as I want to create some chunks of the file at the location of where the file is located and that is to be done from the Backend (Go)

At present, there is no way for the front end to obtain the file content. The current feasible way is to start a service on the back end, and then pass the front end to the back end through the service, just like uploading a file on a web page. Because the current Wails does not provide a good way, this method can only be adopted. accomplish.

@Purple-CSGO
Copy link

It's natural for modern app developers to bring in drag n drop feature. It makes users comfortable to select files or dirs.

@hinupurthakur Could you post a link referring to how the full path is got when it's done? It's definitely an important feature for me to choose the framework.

@hinupurthakur
Copy link
Author

At present, there is no way for the front end to obtain the file content. The current feasible way is to start a service on the back end, and then pass the front end to the back end through the service, just like uploading a file on a web page. Because the current Wails does not provide a good way, this method can only be adopted. accomplish.

@misitebao Currently, yes involvment with the backend is the only work around but a feature for a drag and drop with complete file path would be a really good option for the wails.

@hinupurthakur
Copy link
Author

@hinupurthakur Could you post a link referring to how the full path is got when it's done? It's definitely an important feature for me to choose the framework.

Currently from frontend there isn't any possiblity, The work around I am having is involving backend with frontend to get the fullpath

@leaanthony
Copy link
Member

I suggest you take a look at https://github.com/splode/optimus which has drag and drop capabilities.

@hinupurthakur
Copy link
Author

@leaanthony I have checked the https://github.com/splode/optimus implementation of drag and drop but it is limited to file only. I also need to implement drag and drop for directory (folder).

@leaanthony
Copy link
Member

I suggest changing the ticket title

@hinupurthakur hinupurthakur changed the title [FEAT]: Native drap and drop for file and folder [FEAT]: Native drap and drop for file and folder with complete filepath Feb 2, 2022
@hinupurthakur
Copy link
Author

@leaanthony Can you help with how we can proceed with this issue to the feature implementation?

@leaanthony
Copy link
Member

@hinupurthakur
Copy link
Author

@leaanthony This wasn't helpful for full filepath.

@leaanthony
Copy link
Member

I can't help you with this.

@hinupurthakur
Copy link
Author

@leaanthony So Can we work towards integrating this feature in wails? Or is it not possible at all?

@leaanthony
Copy link
Member

Sure, I'd love this feature, if you want to try and make it work. How is it implemented in other frameworks?

@hinupurthakur
Copy link
Author

@leaanthony I am a backend developer but will research more on it and will provide more information here. Any help from other community members would be really appreciated as well.
Thanks!

@annikovk
Copy link

Electron extends DOM to include path to file: https://www.electronjs.org/docs/latest/api/file-object#file-object

File interface provides abstraction around native files in order to let users work on native files directly with the HTML5 file API.

@leaanthony
Copy link
Member

Yeah, I wonder how they do that?

@leaanthony
Copy link
Member

How did you get on?

@hinupurthakur
Copy link
Author

@leaanthony I would really like to help here to implement something like what electronjs have but probably I will need some guidance there to understand the project better. Let me know if there is a community call or meeting that I can connect on to contribute to this.

@leaanthony leaanthony added this to the v2.5.0 milestone Mar 6, 2022
@stale
Copy link

stale bot commented Apr 16, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wont fix This will not be worked on label Apr 16, 2022
@HKMV
Copy link

HKMV commented Jul 17, 2023

It's awesome, I've wanted this feature for a long time

@AnthoniG
Copy link

This is only Windows though so far isn't it?
No Mac or Linux ?
Also, no timeline for v3?

I wanted to build a file compression program. I can get the entire file if I use the FileReader API, but really I need the full path of the file to read it in via chunks.

@haukened
Copy link
Contributor

A little late to the party, but isn't this what webtkit2gtk allow-universal-access-from-file-urls is for?

Typically each file is considered a separate web origin, I'm pretty sure enabling this flag gives your the real system file path. (Not tested it myself).

webkit2gtk documentation

@jphalip
Copy link

jphalip commented Aug 28, 2023

A little late to the party, but isn't this what webtkit2gtk allow-universal-access-from-file-urls is for?

Typically each file is considered a separate web origin, I'm pretty sure enabling this flag gives your the real system file path. (Not tested it myself).

webkit2gtk documentation

That looks promising indeed. Electron enables that property: https://github.com/electron/electron/blob/d42a94dddeb86d349c050c2ee59b135bd6c8245c/shell/browser/electron_browser_client.cc#L420

@haukened
Copy link
Contributor

I'll see what I can do with this tomorrow! I'd be interested in this as well

@haukened
Copy link
Contributor

I've looked at this, and it looks like some of the assumptions made above are in fact true.
While this setting does allow webkit2 to operate outside the sandbox to some extent, its also true that electron is distributing a modified version of Chromium with an expanded version of the HTML5 File API thats being populated with the native system file path. I don't see how this is currently possible in version 2, unless anyone has some bright ideas? 💡

@rangwea
Copy link

rangwea commented Nov 7, 2023

I'm also waiting for the Native Drap feature. In the v2 version, I can only use this way to implement it: onImagePasted
to upload image byte, and backend method ArticleInsertImageBlob to save image and return path.

@lyimmi
Copy link
Contributor

lyimmi commented Jan 13, 2024

Hi,

I need this functionality for one of my projects and I started implementing it on Linux because that is my most used platform.

As of now I solved the webview stealing my dnd events from the window and crashing my whole window manager issue, by simply calling gtk_drag_dest_unset(G_OBJECT(webview)); on the webview and disabling all dnd events, this way the window can catch all the necessary events.

Here is my progress: https://github.com/lyimmi/wails/tree/feature/1090_native_drag_and_drop_for_file_and_folder

I made two options in the options.App struct:

  • EnableDragAndDrop disables the webview's default drag and drop functionality and enables wails's custom drag and drop events and handlers.
  • DisableWebviewDragAndDrop It can be used to prevent accidental file opening of dragged in files in the webview, when there is no need for drag and drop. (Without cancelling dnd handlers in javascript)

I have implemented two basic methods in the runtime both JS and GO side.
It uses the already existing event system.
The coordinates and the file lists are passed from window.c to the go side via the processMessage() function and after some validation and checking it is emitted to the JS and GO side as well.

The API is pretty linux specific so far, but I'm sure we can solve this on windows and mac as well.

@leaanthony and @stffabi if you have time and take a look at this solution and you think this kind of API could be solved on windows and mac I'm willing to sink some more in time to this and try to solve it on windows.

Edit: I'll check out the windows pull request, maybe the two solution could be merged somehow.

Below I made some examples of the current implementaion.

The GO side:

import "github.com/wailsapp/wails/v2/pkg/runtime"

runtime.DragAndDropOnMotion(ctx, func(x, y int) {
    log.Println("coords", x, y)
})

runtime.DragAndDropOnDrop(ctx, func(paths ...string) {
    log.Println("drop", paths)
})

The JS side:

import {DragAndDropOnDrop, DragAndDropOnMotion} from "../wailsjs/runtime/runtime.js";

DragAndDropOnMotion((x,y) => {
  console.log("coords", x, y);
});

DragAndDropOnDrop(paths => {
  console.log("paths", paths);
});

The JS side (extra):

In javascript we could implement something like the code below. (Bit nicer maybe 😅)

Something similar to the --wails-draggable:drag and --wails-draggable:no-drag style.
It could be --wails-drop-target:drop and --wails-drop-target:no-drop with the stream of coordinates javascript could attach a class e.g wails-drop-target-active to the HTML element when a dragging mouse is over an element that is tagged whit --wails-draggable:drag.

This could be another option in the options.App struct EnableDragAndDropHTMLSomethingSomething.

import {DragAndDropOnDrop, DragAndDropOnMotion} from "../wailsjs/runtime/runtime.js";

let prevEl = null;

DragAndDropOnMotion((x,y) => {
  const el = document.elementFromPoint(x, y);
  const elStyle = el.getAttribute("style");

  if(elStyle !== null && elStyle.indexOf("--wails-drop-target:drop") < 0){
    prevEl = el;
    el.classList.add("wails-drop-target-active");
  } else if(prevEl !== null) {
    prevEl.classList.remove("wails-drop-target-active");
  }
});

DragAndDropOnDrop(paths => {
  if(prevEl !== null) {
    prevEl.value = paths[0];
    prevEl.classList.remove("wails-drop-target-active");
  }
});

Here is a very basic example with the base application and some "advanced" CSS.

Screencast.from.2024-01-13.01-07-58.webm

@lyimmi
Copy link
Contributor

lyimmi commented Jan 14, 2024

I successfully married @ayatkyo's and my solution and it works both on Windows and Linux now. (only tested it on Windows 11 and Ubuntu 23.10 with X)

I dropped the movement coordinate events and changed the options.
When the options.DragAndDrop.EnableWails is set to true the events and handlers are started:

  • On windows javascript handles the drops and sends it to the go side to resolve the file paths based on @ayatkyo work with some minor changes to be compatible with the Linux implementation.

    The whole window is a drop target but the drop will be only accepted on html element tagged with --wails-drop-target:drop

    On mouse hover elements with --wails-drop-target:drop get an html class wails-drop-target-active.
    (The JS code for this is a bit ugly tbh. It was flickering a bit, so I had to make it more complex than I would have liked too.)
  • On linux the implementation is a bit simple GTK pushes the drop event to go and go sends events to JS and go.
  • On mac I think the implementation would be the same as on Linux. But I'm not versed in the magic of macOs develpement.

The options.DragAndDrop.Disable option disables the webview's DnD:

  • On windows I used chromium.AllowExternalDrag(false)
  • On Linux I used gtk_drag_dest_unset(G_OBJECT(webview))

The default runtime handlers can be left out if someone wants to implement a custom handler for the drop events by simply listening to wails.dnd.drop events.

Please try this fork and if its ok I'll make a PR for it.
https://github.com/lyimmi/wails/tree/feature/1090_native_drag_and_drop_for_file_and_folder

It needs some cleaning up but as a test I think its ok.

options:

type DragAndDrop struct {

	// EnableWails enables wails' drag and drop functionality that returns the dropped in files' absolute paths.
	EnableWails bool

	// Disable webview's drag and drop functionality.
	//
	// It can be used to prevent accidental file opening of dragged in files in the webview, when there is no need for drag and drop.
	Disable bool

	// CSS property to test for drag and drop target elements. Default "--wails-drop-target"
	CSSDropProperty string

	// The CSS Value that the CSSDropProperty must have to be a valid drop target. Default "drop"
	CSSDropValue string
}

go runtime method:

runtime.HandleDragAndDrop(ctx, func(paths ...string) {
    log.Println("drop", paths)
})

javascript runtime method:

HandleDragAndDrop(paths => {
    console.log(paths);
});

Edit:
On Linux I have to add the coordinates back because the webview cannot see the dragover.

@kruzr-app
Copy link

how about dragging files out of the window into something like a file explorer?

@lyimmi
Copy link
Contributor

lyimmi commented Jan 14, 2024

how about dragging files out of the window into something like a file explorer?

Well that is another can of worms that I think I'm going leave to someone else. :)

@APshenkin
Copy link
Contributor

@leaanthony @lyimmi

I have some progress on macOS.
I can continue it, just want to confirm, that nobody picked it for now

@leaanthony
Copy link
Member

@APshenkin - Check this out: #3203 (comment) - feel free to branch that and provide updates if you like. The more people commenting/looking at this, the better!

@lyimmi
Copy link
Contributor

lyimmi commented Feb 10, 2024

Hi @APshenkin I don't know what stage @pavelbinar (and his team) are at with it, but as @leaanthony said the more eyes looking at it the better.

For example on windows there is a small window while the dom loads, that the drag and drop prevention is not loaded yet and your drop can still fall trough the original behavior and blow up the state of the program from than on. I have to look into that.

I think we should start the webview with the original D&D blocked and "disable" the wails implementation when the dom is ready. 🤔

@APshenkin
Copy link
Contributor

@leaanthony @lyimmi @pavelbinar

macOS is ready to be reviewed 🎉

Screen.Recording.2024-02-11.at.03.01.25.mov

@Sammy-T
Copy link

Sammy-T commented Jun 17, 2024

Using v2.9.1 on Windows 10, I'm not seeing either the Go or Js callback for OnFileDrop being called.

DragAndDrop: &options.DragAndDrop{
  EnableFileDrop:     true,
  DisableWebViewDrop: true,
},
OnDomReady: func(ctx context.Context) {
  runtime.EventsOn(ctx, "wails:file-drop", func(optionalData ...interface{}) {
    log.Printf("events on file drop: %v", optionalData) // Doesn't get called
  })
  runtime.OnFileDrop(ctx, func(x, y int, paths []string) {
    log.Printf("Dropped at %v,%v\npaths: %v\n", x, y, paths) // Doesn't get called
  })
},

Additionally, DisableWebViewDrop doesn't seem to prevent the default webview open behavior.

@lyimmi
Copy link
Contributor

lyimmi commented Jun 18, 2024

Hi,

I checked it on Win11.

Yes the DisableWebViewDrop does not register a javascript listener on windows to prevent the default behavior. This should be added, mac and linux handles this at the window level.

As far as I can see the current windows implementation requires the --wails-drop-target:drop css style to work on the js side. @jakubpeleska was it intentional? I don't remember if I put it there or not originally.

https://github.com/wailsapp/wails/blob/022a5ffec4a5d956a909796ca62411506847bb0e/v2/internal/frontend/runtime/desktop/draganddrop.js

/**
 * onDrop is called when the drop event is emitted.
 * @param {DragEvent} e 
 * @returns 
 */
function onDrop(e) {
    if (!window.wails.flags.enableWailsDragAndDrop) {
        return;
    }
    e.preventDefault();

    if (!flags.useDropTarget) {   <----- this is the problem
        return;
    }

    // Trigger debounce function to deactivate drop targets
    if(flags.nextDeactivate) flags.nextDeactivate();
.....

However the go events are working fine for me.

@jakubpeleska
Copy link
Contributor

Hi, the condition was already present before my changes.

https://github.com/wailsapp/wails/blame/318628d815d9b97ba2d0438efb34968c1a54acc7/v2/internal/frontend/runtime/desktop/draganddrop.js#L68

I did not encouter this problem as it is specific to Windows. However I think we can simply fix by moving the file resolving before the condition.

@hemkantSplat
Copy link

Using v2.9.1 on Windows 10, I'm not seeing either the Go or Js callback for OnFileDrop being called.

DragAndDrop: &options.DragAndDrop{
  EnableFileDrop:     true,
  DisableWebViewDrop: true,
},
OnDomReady: func(ctx context.Context) {
  runtime.EventsOn(ctx, "wails:file-drop", func(optionalData ...interface{}) {
    log.Printf("events on file drop: %v", optionalData) // Doesn't get called
  })
  runtime.OnFileDrop(ctx, func(x, y int, paths []string) {
    log.Printf("Dropped at %v,%v\npaths: %v\n", x, y, paths) // Doesn't get called
  })
},

Additionally, DisableWebViewDrop doesn't seem to prevent the default webview open behavior.

I have same issue with v2.9.0 none of the events are being called

@leaanthony
Copy link
Member

I did not encouter this problem as it is specific to Windows. However I think we can simply fix by moving the file resolving before the condition.

Would it be possible for you to raise a quick PR for this? 🙏

@FrancescoLuzzi
Copy link
Contributor

I did not encouter this problem as it is specific to Windows. However I think we can simply fix by moving the file resolving before the condition.

Would it be possible for you to raise a quick PR for this? 🙏

I've just opened #3595 👍🏼

@leaanthony
Copy link
Member

leaanthony commented Jul 10, 2024

@FrancescoLuzzi @hemkantSplat - I'm testing this on the master branch and not seeing any issue. Events are being raised in Go and JS...

In my main.go:

		OnDomReady: func(ctx context.Context) {
			runtime.EventsOn(ctx, "wails:file-drop", func(optionalData ...interface{}) {
				log.Printf("events on file drop: %v", optionalData) // Doesn't get called
			})
			runtime.OnFileDrop(ctx, func(x, y int, paths []string) {
				log.Printf("Dropped at %v,%v\npaths: %v\n", x, y, paths) // Doesn't get called
			})
		},

Using https://github.com/beam-transfer/wails-drag-and-drop as a sample project except I had to change the react component otherwise it immediately deregisters the listener:

  useEffect(() => {
    OnFileDrop((x, y, paths) => {
      console.log(x, y, "Dropped files: ", paths);
    }, true);
    // return () => OnFileDropOff();
  }, []);

Output:

2024/07/10 13:37:26 events on file drop: [676 296 [C:\Users\l\Downloads\synergy-win_x64-v3.0.80.1-rc3.msi]]
2024/07/10 13:37:26 Dropped at 676,296
paths: [C:\Users\l\Downloads\synergy-win_x64-v3.0.80.1-rc3.msi]

So I'm unclear now what the linked PR is trying to fix 😅

@FrancescoLuzzi
Copy link
Contributor

@leaanthony My PR covers the case where you pass false when calling OnFileDrop in the JS side.
Since the check on that flag (useDropTarget) is done before computing the file path of the dropped file, the drop event is never triggered

I can't speak about the GO side of things since i've not used it 😄

@Sammy-T
Copy link

Sammy-T commented Jul 11, 2024

So I'm able to get the Go callbacks to run now but I'm seeing some inconsistencies I'm not understanding using the current master branch.

First, I was under the impression that I could use either the Go or Js OnFileDrop() methods as needed but I need to include both if I want the Go methods to run. Is this the intended behavior?

Also, if --wails-drop-target: drop; isn't set on a frontend element:

  • The Go EventsOn(ctx, "wails:file-drop", ...) callback runs as expected
  • The Go OnFileDrop() callback runs as expected
  • The Js EventsOn('wails:file-drop', ...) callback runs as expected
  • But the Js OnFileDrop() does not run the passed in callback

which doesn't make sense to me since the Js OnFileDrop() method needs to be used for any of it to work.

@FrancescoLuzzi
Copy link
Contributor

FrancescoLuzzi commented Jul 11, 2024

First, I was under the impression that I could use either the Go or Js OnFileDrop() methods as needed but I need to include both if I want the Go methods to run. Is this the intended behavior?

  • But the Js OnFileDrop() does not run the passed in callback

Yes it is intended, but only if you pass the useDropTarget as true
https://github.com/FrancescoLuzzi/wails/blob/master/v2/internal/frontend/runtime/desktop/draganddrop.js#L220-L229

This helps you implement the drop behavior without checking that the drop location is inside the bounds of the element that you intend to drop on, which would be something like:

useEffect(() => {
    OnFileDrop((x, y, paths) => {
      // useRef that bad boy
      const bb = refDrop.current.getBoundingClientRect();
      if ( x >= bb.left && x <= bb.right && y >= bb.bottom && y <= bb.top){
         // do something with paths
      }
    }, false); // we want to handle this manually so set useDropTarget to false
    return () => OnFileDropOff();
  }, []);

tldr pass false to OnFileDrop if you don't care where the user drops the file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement New feature or request TODO The issue is ready to be developed
Projects
None yet