gtk-rs / gtk3-rs

Rust bindings for GTK 3
https://gtk-rs.org
MIT License
507 stars 90 forks source link

[HELP] Implementing TreeView's drag and drop for files (documents / folders) #570

Closed smailbarkouch closed 3 years ago

smailbarkouch commented 3 years ago

Hello, I am trying to use TreeView to show files (documents / folders). In this treeview I want to enable drag and drop but I don't want to allow files to be dropped into documents.

I tried to stop this behavior by passing a closure to connect_drag_drop. This is a method a part of TreeView that would signal when an item was dropped. In this closure I would detect whenever the drop destination would be in a document. I did this by calling another method from TreeView, get_drag_dest_row, that would return where the drop destination would be. If it was going to be in document, I would change the default destination to next to the document by calling set_drag_dest_row and returning Inhbit(true). If it wasn't, I would allow the default behavior by returning Inhibit(false). Unfortunately this did not change its destination, and files were still able to be dropped in documents.

I then thought I should stop the drop earlier by connecting a closure to connect_drag_motion. Unfortunately, it did not work as well.

Based on the GTK docs, someone in this situation would override row_drop_possible, from TreeStore, to stop documents from being a destination.

It seems though, I am not able to subclass it since it does not have an impl class, as hinted from this video. It looks like I have to open an issue to get this fixed. Am I thinking about this properly?

bilelmoussaoui commented 3 years ago

row_drop_possible is part of the TreeDragDest interface. You will probably have to create a custom TreeView and implement TreeDragDest. You can borrow the subclassing code for both from https://github.com/gtk-rs/gtk4-rs/blob/master/gtk4/src/subclass/tree_drag_dest.rs & https://github.com/gtk-rs/gtk4-rs/blob/master/gtk4/src/subclass/tree_view.rs and backport them to gtk3.

smailbarkouch commented 3 years ago

Thanks for responding. I tried to borrow the subclassing code from the links you shared but I can't seem to get it working. One problem, for instance, is with impling IsSubclassable<T>. Both gtk4 and gtk3 have different ways they do this, and tried to mimic what I saw in gtk3:

unsafe impl<T: ObjectSubclass + TreeViewImpl> IsSubclassable<T> for TreeViewClass {

When I do this though, I get two compiler errors:

error[E0119]: conflicting implementations of trait `gio::subclass::IsSubclassable<_>` for type `gtk::TreeViewClass`:

and

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)

I am also confused on why I would be sub-classing TreeView if row_drop_possible is a part of the TreeDragDest, which is implemented by TreeStore. As shown here. Wouldn't it be easier to subclass TreeStore and override that method?

bilelmoussaoui commented 3 years ago

Ah never mind what said above. Indeed TreeDragDest has to be implemented for a TreeModel and not a TreeView. The issue with subclassing a TreeModel is that it can't be done with safe Rust as far I know. See https://github.com/gtk-rs/gtk4-rs/pull/169 which contains the code for it. It would require someone familiar with the Tree* API to figure out how it can be used from Rust.

The issue is that you can't create a custom TreeStore as in subclass TreeStore. You need to subclass TreeModel and implement the TreeDragDest interface

smailbarkouch commented 3 years ago

Ah okay. The reason I was mentioning subclassing TreeStore is because it already is complete in itself, all I need it to do is differentiate files. If I were to implement TreeModel though, I would have to do a lot of things from the ground up just to create something that is like TreeStore. That would include creating it to have a hierarchical tree structure and implementing all of its other interfaces. None that I am honestly familiar with, and it kinda seems like a waste.

I know that I can't subclass TreeStore currently because there is currently no TreeStoreImpl, but I was hoping to make a feature request for it since it should be possible (as far as I know). Then I could just override row_drop_possible. Is that, by any chance, possible?

bilelmoussaoui commented 3 years ago

You can't subclass TreeStore as I said above, it's not a subclass-able type.

bilelmoussaoui commented 3 years ago

I suggest asking a question discourse.gnome.org/ how would be the right approach to override only row_drop_possible and if there's something missing on the bindings side, we can help by either implementing or putting you in the right direction.

I'm very not familiar with such API so I can't help here. Sorry

smailbarkouch commented 3 years ago

Apologies for the late response. I ended up finding a solution after a long while. It turns out I wasn't really using the convince function set_reorderable correctly. Its a function part of TreeView that automatically adds drag and drop, but unfortunately you can't override its default behaviors. This meant that regardless of whatever changes I made to get my desired behavior with documents, it wouldn't work. I didn't realize this at first, but once I did I implemented drag and drop from the low level API I got exactly what I wanted. Thank you again for the help!