Allow dragging files/folders to View mode
ClosedPublic

Authored by huoni on Apr 2 2018, 7:12 AM.

Details

Summary

Specifically accepts drop events within the viewport of View mode.
If a folder that contains images is dropped, it will open that folder
and remain in View mode with the first image selected. If the
folder doesn't contain images, it switches to Browse mode.
Dragging multiple items is supported but only the first item is opened.

BUG: 169408
FIXED-IN: 18.08.0

Test Plan

Test with different image formats, as well as unsupported format (Gwenview
should display a nice error).
Test with folder (with and without images within).
Browse mode and the Thumbnail Bar drag drop should work as before.
Drop operations should not be accepted anywhere else in the GUI where they aren't
already supported. In other words, this should only add drop functionality to the
View viewport.

Ensure drops are only accepted for URL mimetypes (e.g. ignore text).

Diff Detail

Repository
R260 Gwenview
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
huoni requested review of this revision.Apr 2 2018, 7:12 AM
huoni created this revision.
huoni edited the summary of this revision. (Show Details)Apr 2 2018, 7:13 AM
huoni added a project: Gwenview.
huoni added inline comments.Apr 2 2018, 7:21 AM
app/viewmainpage.cpp
786–802

I decided to handle the events here because the events had to be handled at the QGraphicsScene, but we needed access to MainWindow to open the URL. I felt putting it in ViewMainPage was a nice middle ground, and because it relates to View mode.

lib/documentview/documentviewcontainer.cpp
114 ↗(On Diff #31141)

Events 'start' at the scene, so we filter them. We don't handle the events here normally because we need access to MainWindow.

125 ↗(On Diff #31141)

DocumentViews are what's actually visible, so we have to accept drops here.

lib/documentview/documentviewcontainer.h
49 ↗(On Diff #31141)

Could have left this as parent = 0, but I felt this helped make things clear, and is simpler than actually passing a reference to ViewMainPage in addition to the parent.

huoni edited the test plan for this revision. (Show Details)Apr 2 2018, 7:24 AM
huoni updated this revision to Diff 32034.Apr 13 2018, 3:16 AM
  • rebase
huoni updated this revision to Diff 32035.Apr 13 2018, 3:25 AM
  • Explicitly pass ViewMainPage to DocumentViewContainer
lib/documentview/documentviewcontainer.h
49 ↗(On Diff #31141)

I changed my mind here, and concluded it was best to pass ViewMainPage explicitly.

huoni edited the summary of this revision. (Show Details)Apr 13 2018, 3:26 AM
rkflx requested changes to this revision.May 19 2018, 5:04 PM

Thanks for the patch :) From a behavioural standpoint it works very well, and you seem to have covered a lot of edge cases.

Nevertheless, I have a couple of points.


I'm not able to compile all of Gwenview:

part/gvpart.cpp: In constructor ‘Gwenview::GVPart::GVPart(QWidget*, QObject*, const QVariantList&)’:
part/gvpart.cpp:64:78: error: no matching function for call to ‘Gwenview::DocumentViewContainer::DocumentViewContainer(QWidget*&)’
     DocumentViewContainer* container = new DocumentViewContainer(parentWidget);
                                                                              ^

Note that this might indicate a larger problem with your patch, i.e. ViewMainPage might not be the best place to implement drop handling. Using Gwenview's KPart in Konqueror should also support dropping of URLs (at least in theory, not sure if this would need more work, so likely out-of-scope for this patch).

I don't see yet why you'd need to use an eventFilter and add a dependency on ViewMainPage. Normally reimplementing the corresponding event like it's done in ThumbnailView::dropEvent should be enough.

Qt's docs advice

To handle the incoming drag, reimplement QGraphicsItem::dragEnterEvent()

Perhaps AbstractImageView would be a good candidate, because it already handles key and mouse events, and is (indirectly) derived from QGraphicsItem. If you don't have direct access to openDirUrl there, perhaps some signal/slot magic will work then (e.g. see previousImageRequested)?


When trying to drop text dragged from Kate, Gwenview aborts:

ASSERT: "!isEmpty()" in file /usr/include/qt5/QtCore/qlist.h, line 345
Aborted (core dumped)

It should be enough to reject unsupported mimetypes in the first place, just like Browse is already showing the "cannot drop here" cursor in that case.

lib/documentview/documentviewcontainer.cpp
127 ↗(On Diff #32035)

Would it make more sense to put this in the constructor of DocumentView?

This revision now requires changes to proceed.May 19 2018, 5:04 PM
huoni planned changes to this revision.May 23 2018, 5:14 AM

Thanks for the patch :) From a behavioural standpoint it works very well, and you seem to have covered a lot of edge cases.

Nevertheless, I have a couple of points.


I'm not able to compile all of Gwenview:
part/gvpart.cpp: In constructor ‘Gwenview::GVPart::GVPart(QWidget*, QObject*, const QVariantList&)’:
part/gvpart.cpp:64:78: error: no matching function for call to ‘Gwenview::DocumentViewContainer::DocumentViewContainer(QWidget*&)’
     DocumentViewContainer* container = new DocumentViewContainer(parentWidget);
                                                                              ^

Note that this might indicate a larger problem with your patch, i.e. ViewMainPage might not be the best place to implement drop handling. Using Gwenview's KPart in Konqueror should also support dropping of URLs (at least in theory, not sure if this would need more work, so likely out-of-scope for this patch).

I don't see yet why you'd need to use an eventFilter and add a dependency on ViewMainPage. Normally reimplementing the corresponding event like it's done in ThumbnailView::dropEvent should be enough.

Qt's docs advice

To handle the incoming drag, reimplement QGraphicsItem::dragEnterEvent()

I definitely tried this initially, but it didn't work. My memory fails me as to the specifics, but the only way I could get it to work was by handling the event at the QGraphicsScene level (which is in DocumentViewContainer).
I'll definitely have another look at it though.

Perhaps AbstractImageView would be a good candidate, because it already handles key and mouse events, and is (indirectly) derived from QGraphicsItem. If you don't have direct access to openDirUrl there, perhaps some signal/slot magic will work then (e.g. see previousImageRequested)?

AbstractImageView might not work, but I'll aim for that if possible. As I said, my memory fails me, but I think the events weren't getting that far down in the hierarchy. Perhaps DocumentViewContainer would be a good alternative.

When trying to drop text dragged from Kate, Gwenview aborts:
ASSERT: "!isEmpty()" in file /usr/include/qt5/QtCore/qlist.h, line 345
Aborted (core dumped)

It should be enough to reject unsupported mimetypes in the first place, just like Browse is already showing the "cannot drop here" cursor in that case.

Will investigate.

TODO:

  • Move code as close to AbstractImageView as possible, use signals/slots to get back to MainWindow
  • Fix dropping unsupported mimetypes e.g. text
  • Fix compile error
rkflx added a comment.May 23 2018, 9:17 PM

I definitely tried this initially, but it didn't work. My memory fails me as to the specifics, but the only way I could get it to work was by handling the event at the QGraphicsScene level (which is in DocumentViewContainer).
I'll definitely have another look at it though.

Maybe you ran into something like https://forum.qt.io/topic/3092/qgraphicsscene-drag-and-drop (relating to class AbstractImageView : public QGraphicsWidget)?

huoni updated this revision to Diff 34952.May 27 2018, 4:18 AM
huoni marked an inline comment as done.
huoni edited the test plan for this revision. (Show Details)
  • Handle drop events in DocumentView
  • Fix crash when dragging non-URL mimetypes
  • Fix compile error
huoni added a comment.May 27 2018, 4:18 AM

Maybe you ran into something like https://forum.qt.io/topic/3092/qgraphicsscene-drag-and-drop (relating to class AbstractImageView : public QGraphicsWidget)?

Thanks, this was indeed the problem.

With that I have greatly reduced the complexity of this patch. I decided to put the event handling in DocumentView instead of AbstractImageView because I think this makes more logical sense, and shortens the signal/slot chain.
But there's one problem I haven't been able to solve - accepting drops when a video is displayed. Something to do with QGraphicsProxyWidget not forwarding the events, or something along those lines. I'm not sure if this is a show stopper though, given Gwenview's primarily an image viewer, not a video viewer.

With that I have greatly reduced the complexity of this patch.

\o/

But there's one problem I haven't been able to solve - accepting drops when a video is displayed. Something to do with QGraphicsProxyWidget not forwarding the events, or something along those lines. I'm not sure if this is a show stopper though, given Gwenview's primarily an image viewer, not a video viewer.

Hm, adding the corresponding override I could get it to show the "accept" cursor briefly, but then it crashed in some kind of endless event loop. Let's not worry about it.

app/mainwindow.cpp
325

I don't see why you'd need QObject here?

lib/documentview/documentview.cpp
836

As QGraphicsWidget is the direct base class of DocumentView, why not call the corresponding event here too?

843

Just an idea (nothing for this patch): It would be kinda nice to open Compare mode if there are not too many images and they are from the same directory. (Related: Passing multiple images when starting from the shell or from Dolphin.)

844

const please ;)

huoni updated this revision to Diff 35375.Jun 1 2018, 11:31 PM
huoni marked 3 inline comments as done.
  • const
  • Remove unnecessary QObject::
  • Call QGraphicsWidget instead of QGraphicsItem in drag events
  • Rebase
huoni added a comment.Jun 1 2018, 11:33 PM

But there's one problem I haven't been able to solve - accepting drops when a video is displayed. Something to do with QGraphicsProxyWidget not forwarding the events, or something along those lines. I'm not sure if this is a show stopper though, given Gwenview's primarily an image viewer, not a video viewer.

Hm, adding the corresponding override I could get it to show the "accept" cursor briefly, but then it crashed in some kind of endless event loop. Let's not worry about it.

That brief accept cursor is I believe from the underlying DocumentView. If you don't add the override, and slowly drag into the viewport you'll see the cursor flash accept.
I also noticed we run into the same issue with the "Gwenview cannot display this type of file" error, since MessageViewAdapter also uses a QGraphicsProxyWidget.

app/mainwindow.cpp
325

Ah, I didn't notice MainWindow::Private struct was actually a member of MainWindow (they are usually separate). Qt Creator puts up a warning for all the naked connects complaining of too many arguments, so I thought something weird was going on.

rkflx accepted this revision.Jun 2 2018, 6:06 AM

Thanks, LGTM now :)

I also noticed we run into the same issue with the "Gwenview cannot display this type of file" error, since MessageViewAdapter also uses a QGraphicsProxyWidget.

That's an important hint, as we can rule out Phonon's video widget now. Anyway, something for another time to investigate…

This revision is now accepted and ready to land.Jun 2 2018, 6:06 AM
rkflx added inline comments.Jun 2 2018, 6:56 PM
app/mainwindow.cpp
325

Switching to the Clang code model in Qt Creator should resolve the warning. It's pretty nice compared to the simpler built-in code model: https://doc.qt.io/qtcreator/creator-clang-codemodel.html

This revision was automatically updated to reflect the committed changes.