GNOME Bugzilla – Bug 408327
Improving tooltip positioning
Last modified: 2007-07-22 22:32:32 UTC
when moving the mouse fast from the labels to the treeview, one can end up with the tooltip for the tree rows being displayed over the labels. That looks odd. And it is aggravated by the tooltip position not updating when moving from row to row.
This basically boils down to a single question: when should we update the position of a tooltip? We can always update the tooltip position, which means that the tooltip will follow the mouse. Nicer might be to only update the position of the tooltip when its contents change -- and for this case we would need a good way to determine when exactly those contents change. I have been thinking about this issue during the implementation process, but didn't manage to come up with a nice answer back then.
*** Bug 449083 has been marked as a duplicate of this bug. ***
Adding a related bug as duplicate of this bug. I've been aware that the tooltip positioning on tree views, etc is sub par since before I committed the stuff to trunk. Fixing this is by no means an easy problem. One way to get rid of all issues is to let the tooltip follow the mouse pointer. IMHO this would be very ugly/evil/etc. I've been quickly looking at how other toolkits solve this problem. For example in mozilla list views, the tooltip for a row is hidden as soon as the pointer leaves the row. When the pointer comes to rest at a new row, the tooltip delay timeout seems to be restarted. It feels like mozilla treats rows as "separate widgets" all having their own tooltip. Cocoa has specialized methods for handling tooltips on cells, etc. For their TableView this method has been added to just the table view (basically it is a kind of signal which asks you to provide a string to display as tooltip for a given cell/column/row). For GTK+, I think having the code reposition the tooltip when the tooltip's contents change is close to impossible, since it is very hard to catch all possible changes to a tooltip. I think we need a way for widgets like GtkTreeViews, canvases, etc to be able to provide some kind of hint about when to reposition the tooltip. For a GtkTreeView this hint would, for example, provide where the different rows/cells are, so the tooltip code will be able to figure out when the pointer moves to a different "item" so the tooltip can be updated and repositioned accordingly.
Tim asked me to outline the problems I see with detecting tooltip changes, so here I go. AFAICS basically you want to know if a visible tooltip changes between subsequent calls to query-tooltip. If a tooltip goes from hidden to visible, you always want to reposition it. When the contents of a tooltip change, there is a good change that the "context of the tooltip" changed (ie. it is pointing at another "distinguishable item" in the same widget) so you want to place the tooltip at another position. (In tree and icon views this can be seen as moving the mouse pointer to another row/cell or icon/item). Detecting changes in tooltip contents is hard: - For custom windows it is impossible to do for GTK+ at all. - Detecting changes in content of the custom widget embedded in a GtkTooltip is hard/impossible. - The GtkTooltip is reset at every requery. If we want to detect whether the same string, pixbuf, etc is set between subsequent calls to query-tooltip, we will have to cache those values and compare. Even then this wouldn't be fully fool-proof, if you have two different items in the same widget with the same tooltip contents (for both of which you want to reposition the tooltip: think two cells with the same tooltip, or two windows of the same problem with the same window title in the window list) you are still busted.
This idea just came to mind, so I just had to write it down somewhere. It occurred to me that it would be well possible to have the user set this "positioning hint" in the query-tooltip callback. It could either be something like: gtk_tooltip_set_position_preference (tooltip, x, y) [this would specify an (x, y) where the tooltip could be positioned] or gtk_tooltip_set_position_hint (tooltip, rect) [this would specify a rectangle around the item for which these tooltips contents are meant. This could be a tree view row/cell, icon view item, window list item, etc, etc.] Not sure if this is a good idea at all. Right now I would say I prefer the second option and think it would actually work.
*** Bug 453172 has been marked as a duplicate of this bug. ***
Created attachment 91497 [details] [review] quick implementation of gtk_tooltip_set_position_hint() approach I played around with the gtk_tooltip_set_position_hint() approach today, resulting in this patch. It works by hiding the tooltip once the pointer leaves the area of interest (the widget itself, or a user-supplied "context area"/position hint). The behavior of the tooltips on GtkToolbars (try the "Application main window" demo in gtk-demo) is much better. If you add the following lines to the query_tooltip_tree_view_cb() in testtooltips, you get nicely behaving tooltips on tree views: gtk_tree_view_get_background_area (tree_view, path, NULL, &rect); gtk_tree_view_convert_tree_to_widget_coords (tree_view, NULL, rect.y, NULL, &rect.y); rect.x = GTK_WIDGET (tree_view)->allocation.x; rect.width = GTK_WIDGET (tree_view)->allocation.width; gtk_tooltip_set_position_hint (tooltip, &rect); This approach is very flexible, though it feels like it will result in a good amount of code duplication... For per-row tooltips, each tree view needs those 7 lines of code above in the query-tooltip callback.
Playing the gtk-demo a bit, I have to say that I find it a little bit irritating that the horizontal positioning of the tooltip depends on where I enter the toolitem from and how fast I move. The treeview positioning in testtooltips is in fact nice. I guess we can always add a convenience function like gtk_tooltip_set_treeview_position_hint (GtkTooltip *tip, GtkTreeView *treeview, GtkTreePath *path) later
(In reply to comment #8) > Playing the gtk-demo a bit, I have to say that I find it a little bit > irritating that the horizontal positioning of the tooltip depends on where > I enter the toolitem from and how fast I move. The position function which is used right now, places the tooltip below the mouse pointer at the right (I think this is what Soeren suggested in one of his mails). This position is calculated as soon as the "delay timeout" expires. When moving from button to button in the toolbar, this timeout can be very short because of the browse timeout (which is probably exactly what you want on a toolbar and other "grouped" widgets). > The treeview positioning in testtooltips is in fact nice. I guess we can > always add a convenience function like > > gtk_tooltip_set_treeview_position_hint (GtkTooltip *tip, > GtkTreeView *treeview, > GtkTreePath *path) > > later Yeah, was thinking of convenience functions too. Does need some more thought, because some people will want to have per-cell tooltips, or group cells, etc. But having one basic one which can cover the basic needs would be good.
(In reply to comment #5) > This idea just came to mind, so I just had to write it down somewhere. > gtk_tooltip_set_position_hint (tooltip, rect) > [this would specify a rectangle around the item for which these tooltips > contents are meant. This could be a tree view row/cell, icon view item, > window list item, etc, etc.] i agree that we need tooltip API to specify sub-widget tip areas. i'd like to suggest to change the name accordingly though ("hint" is much too vague i think): void gtk_tooltip_set_tip_area (GtkTooltip*, GdkRectangle*); (In reply to comment #8) > The treeview positioning in testtooltips is in fact nice. I guess we can > always add a convenience function like > > gtk_tooltip_set_treeview_position_hint (GtkTooltip *tip, > GtkTreeView *treeview, > GtkTreePath *path) hum, i think that is badly misnamed. every big widget that has areas of interest smaller than the actual widget size will need such functionality, so this clearly doesn't belong into the gtk_tooltip namespace. given the above tip_area function and Kris' comments about cells, it makes sense to also add: void gtk_treeview_set_tooltip_row (GtkTreeview*,GtkToolip*,GtkTreePath*); void gtk_treeview_set_tooltip_cell (GtkTreeview*,GtkToolip*,GtkTreePath*,int); etc. particularly for rows, i think we should have this API right away, it's clear that people will want to set tooltips on rows already.
Created attachment 91736 [details] [review] patch as committed Patch as committed. Leaving the bug open for review.
hmm, it is actually less convenient than one might expect, looking at the callback in testtooltips.c: if (keyboard_tip) { /* Keyboard mode */ gtk_tree_view_get_cursor (tree_view, &path, NULL); if (!path) return FALSE; } else { gint bin_x, bin_y; gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &bin_x, &bin_y); /* Mouse mode */ if (!gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y, &path, NULL, NULL, NULL)) return FALSE; } will pretty much have to be repeated in every implementation. Maybe a gboolean (* query_treeview_tooltip) (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, GtkCellRenderer *renderer); would allow to avoid that code repetition, but a "convenience signal" is a bit harder to do.
(In reply to comment #12) > hmm, it is actually less convenient than one might expect, looking at the > callback in testtooltips.c: > > if (keyboard_tip) > { > /* Keyboard mode */ > gtk_tree_view_get_cursor (tree_view, &path, NULL); > > if (!path) > return FALSE; > } > else > { > gint bin_x, bin_y; > > gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, > &bin_x, &bin_y); > > /* Mouse mode */ > if (!gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y, > &path, NULL, NULL, NULL)) > return FALSE; > } > > > will pretty much have to be repeated in every implementation. nah, it looks more like there's a bit more API needed to make treeviews really convenient. the query_tooltip_text_view_cb() seems to suggest the same for GtkTextView btw. e.g. for tree view, the callback could read: if (!gtk_tree_view_get_tooltip_args (tree_view, x, y, keyboard_tip, &model, &path, &iter)) return FALSE; tip_string = g_strdup_printf (...); gtk_tooltip_set_markup (tooltip, tip_string); gtk_tree_view_set_tooltip_row (tree_view, tooltip, path); gtk_tree_path_free (path); g_free (tip_string); return TRUE; or maybe s/get_tooltip_args/get_tooltip_context/... > Maybe a > gboolean (* query_treeview_tooltip) (GtkTreeView *treeview, > GtkTreePath *path, > GtkTreeViewColumn *column, > GtkCellRenderer *renderer); > would allow to avoid that code repetition, but a "convenience signal" > is a bit harder to do. i think this would be the entirely wrong approach, when you could simply have 1 or 2 treeview convenience functions instead that implement the tooltip logic handling for you.
We also have gtk_tree_view_get_tooltip_context() now and the uber-cool gtk_tree_view_set_tooltip_column(). Resolving as FIXED.
*** Bug 457984 has been marked as a duplicate of this bug. ***