GtkTreeView drag-and-drop API
- From: Federico Mena Quintero <federico ximian com>
- To: GTK+ development mailing list <gtk-devel-list gnome org>
- Cc: GNOME Desktop <desktop-devel-list gnome org>
- Subject: GtkTreeView drag-and-drop API
- Date: Thu, 26 Feb 2004 13:03:54 -0600
Hi,
I have read through Kris's patch for multi-row DnD in GtkTreeView, and
I have a bunch of comments. For reference, the patch is here:
http://mail.gnome.org/archives/gtk-devel-list/2003-December/msg00160.html
I'm CC-ing desktop-devel-list because I'm sure people there will have
things to say about DnD in GtkTreeView as well.
The general requirements for DnD in trees are as follows:
1. The API should be able to do everything that the normal DnD API for
widgets can do, but it should make things easier for trees --- for
example, operating with GtkTreePaths rather than plain (x, y)
coordinates.
2. Right now the tree drag interfaces only have methods, not signals.
This makes it hard to provide custom DnD functionality from
applications, especially if one uses GtkListStore or GtkTreeStore
--- one has to derive a new class from them, and re-install the
drag source/dest interfaces. With signals this would be much
easier.
3. Right now, the data models have to carry the drag source/dest
interfaces with them, but yet one must call
gtk_tree_view_enable_model_drag_source() and
gtk_tree_view_enable_model_drag_dest() on the *view*, not the
model. These functions take an array of GtkTargetEntry, which
means that the view-side caller needs to know about which data
types the model can import/export. I think this separation is
wrong.
4. If we have methods/signals to make the normal GtkWidget drag
signals easier for tree views, they should have access to the
GdkDragContext. This is especially necessary to notify about
legal drag actions from the destination side, e.g. situations
where you do not pass GTK_DEST_DEFAULT_MOTION and you call
gdk_drag_status() by hand --- you can move/copy/link a dragged
file that is hovering over a directory, but you can only "copy" if
it is being dragged into an executable.
Source-side API:
5. The available data types and drag actions may change depending on the
selection. However, the simple GtkTreeDragSource::row_draggable()
method does not let one specify these values from within --- or is
one supposed to call gtk_tree_view_enable_model_drag_source() with
each selection change in order to reset the target list?
I think it would be better to remove/deprecate the
gtk_tree_view_enable_model_drag_source() function, and add the
following:
- A function:
void gtk_tree_view_enable_drag_source (GtkTreeView *tree_view);
- A signal in GtkTreeViewClass:
gboolean maybe_begin_drag (GtkTreeView *tree_view,
gint button,
GdkEvent *event);
This would get emitted somewhere in GtkTreeView::motion_event
after crossing the drag threshold, but only if
gtk_tree_view_enable_drag_source() has been called beforehand.
The application could then see if it makes sense to start a
drag, e.g. by looking at the selection. If starting a drag
makes sense, it should set up whatever state is necessary, and
call a function like
GdkDragContext *gtk_tree_view_drag_begin (GtkTreeView *tree_view,
GtkTargetList *targets,
GdkDragAction actions,
gint button,
GdkEvent *event);
The "button" and "event" parameters come from the
::maybe_begin_drag() signal, of course. This function is
analogous to gtk_drag_begin(), and the difference is that it
would create a reasonable drag pixmap cursor by default. The
tree view, of course, would set up whatever drag state it needs
and call gtk_drag_begin() on its own.
6. Why do we need GtkTreeDragSource::drag_data_get() and
::multi_drag_data_get()? The normal GtkWidget::drag_data_get()
should be enough; moreover, it does expose the GdkDragContext.
The "path" and "ref_list" arguments, respectively, are anologous
to looking at the tree selection by hand from within the signal
handler.
7. Similar to (6), do we need GtkTreeDragSource::drag_data_delete()
and ::multi_drag_data_delete()? The normal
GtkWidget::drag_data_delete() should be enough; again, one can
look at the selection to see which rows need to be considered.
Destination-side API:
8. Similar to (5), I am not sure that gtk_tree_view_enable_model_drag_dest()
is the best way to do things --- why is it tied to the model?
Analogous to (5), perhaps we should just rename it to
void gtk_tree_view_enable_drag_dest (GtkTreeView *tree_view,
const GtkTargetEntry *targets,
gint n_targets,
GdkDragAction actions);
9. The existing GtkTreeDragDest::row_drop_possible() is not
sufficient, as it has the following limitations:
- It does not expose the GdkDragContext, nor the timestamp, so
we cannot use ::row_drop_possible as an equivalent to the
GtkWidget::drag_motion signal and call gdk_drag_status() on
our own.
- It does not tell us whether the user wants to drop on a row,
or before/after it; that is, it does not make use of
GtkTreeViewDropPosition. Since it does not expose the actual
drop coordinates, either, we cannot figure this out by hand
with gtk_tree_view_get_dest_row_at_pos().
The usage case I'm envisioning is as follows. Say you have a list
of text strings, displayed in a tree view. You want to be able to
do the following things:
- Reorder the strings within the list.
- Drag strings from the list to the outside world, exporting
them as UTF8_STRING.
- Drag in strings from the outside. You can either drop a string
between rows to create a new entry, or drop it in an existing
row to change its contents.
To do this, your handler would look like this:
static GtkTreeViewDropPosition
my_row_drop_possible_handler (GtkTreeView *tree_view,
GdkDragContext *context,
GtkTreePath *path,
GtkTreeViewDropPosition position,
guint time_)
{
GdkDragAction action;
gboolean valid;
valid = FALSE;
if (has_target (context, INTERNAL_REORDER) &&
(context->actions & GDK_ACTION_MOVE))
{
valid = TRUE;
action = GDK_ACTION_MOVE;
/* Fix up tentative position */
if (position == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE)
position = GTK_TREE_VIEW_DROP_BEFORE;
else if (position == GTK_TREE_VIEW_DROP_INTO_OR_AFTER)
position = GTK_TREE_VIEW_DROP_AFTER;
}
else if (has_target (context, UTF8_STRING) &&
(context->actions & GDK_ACTION_COPY))
{
valid = TRUE;
action = GDK_ACTION_COPY;
/* No need to fix up the position, as it *may* go into
* an existing row rather than between rows.
*/
}
if (valid)
{
gdk_drag_status (context, action, time_);
return position;
}
else
return GTK_TREE_VIEW_DROP_POSITION_INVALID;
}
Note the addition of GTK_TREE_VIEW_DROP_POSITION_INVALID.
10. Do we need a special way to indicate that the user wants to drop
on the blank area of a tree view, or is this handled with
GTK_TREE_VIEW_DROP_AFTER plus the path of the last row in the
tree? What happens when the tree is empty?
11. In addition to the GdkDragContext and the timestamp, the
::row_drop_possible() handler may need to know the exact column,
cell renderer and position over which the cursor is hovering. A
pixbuf cell may want to change the pixbuf to an "armed" version
when you hover on it. I'm not sure if this is necessary.
12. We may need an equivalent of the GtkWidget::drag_leave() signal
for rows if we add (11); that would be when you reset the "armed"
icon to the normal one.
13. Same as before, the GtkTreeDragDest::drag_data_received() needs to
be able to see the GdkDragContext to determine the proper action
(which one among move/copy/link/ask in a file manager, for
instance).
14. I have never had the need to implement my own ::drag_drop()
handler in normal widgets, so I don't know if we need a similar
signal in the DnD interfaces for trees.
In summary:
- It would be better to have signals, rather than methods only, for
the DnD interfaces. This would make them easy for casual use.
- All the tree-specific drag signals need to have access to the
GdkDragContext, plus any extra tree-specific information such as the
GtkTreeViewDropPosition.
- We need to be able to fix up positions --- a suggested "drop before
or inside" can turn into "drop inside". We also need to be able to
say "this is an invalid position, can't drop here"; see (9) and the
comment at the end.
- I think it's wrong to tie the drag interfaces to the model. This
forces the programmer to know which drag types it supports when
calling gtk_tree_view_drag_model_enable_*().
I hope these comments are useful in designing a better API :)
Federico
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]