GNOME Bugzilla – Bug 141937
TreeViewColumn context menus are hard to implement
Last modified: 2018-02-10 03:43:17 UTC
As far as I can tell, the only way to implement a right-click context menu on a TreeView column header is to use a custom widget with the GtkTreeViewColumn. But that means you have to reimplement far too much stuff.
Connecting to the button_press_event signal on column->button should work fine. Or you can just use the clicked signal on the column.
OK. Thanks. If that's public API, then maybe it should be documented. I don't think the clicked signal will be any good for a right-click context menu.
(See also bug 148558, which looks like a dupe.) Hmm... I see problems with this. ('button-press-event', not 'clicked', because that's not how popup menus are used.) 1. The field column->button isn't wrapped by the bindings (like PyGTK and java-gnome) and isn't documented in the C API, so you have to do a dance to get it (unless you're in C/C++ and don't mind using parts of the API only documented in the header files). In languages other than C/C++, you have to call column.set_widget() with your own label, and then call get_parent() on that repeatedly until you come to a gtk.Button object. (Note that get_widget() gives you column->child, which is different, and connecting signals to it doesn't work. You need column->button. And get_widget() returns NULL if you haven't set a custom widget, so it doesn't help the bindings: they need to set a custom widget to get at this field.) Since it's a private field that's required for a fairly common task, it would seem to make sense to give it a normal accessor (get_button()?) -- this would also encourage bindings to wrap it. And that starts to make the API more complex, which is why simply having a 'popup-menu' event would be nice. (Instead of exposing more of the internals, and providing more documentation on how to assemble them to make a popup menu, just provide popup menus.) 2. It has bugs that make it unusable. If your columns have set_reorderable(True), then after your popup menu goes away, when you mouse over the column header, it starts moving (as if you were dragging it, even though you aren't pressing the mouse button). So you have to choose between being able to drag your columns, and being able to right-click them. (Or you could connect to button-release-event, but that's as bad as 'clicked'.) It's also not obvious to me that this can easily be solved with the current API. The mouse event seems to get to the treeview immediately, and there's no way (AFAICT) for a column or popup to stop that. (Add another function to the treeview to tell it a popup has been shown? You could, I guess, but again, that's more API complexity. All you really want is to just tell the column to handle right-clicks, give you popup events, and deal with all the details for you.) 3. It has unnecessary and undocumented restrictions. You have to set_clickable(True), even if you don't otherwise want them to be clickable. (Though this is probably relatively easy to fix.) If this is the "official" Gtk+ way to do it, I'll file bugs for these issues (accessor/bindings/documentation, bugs/restrictions), but it seems to me that a better solution would be to make the interface simpler and easier on programmers by adding another signal to GtkTreeViewColumn. Even if the current scheme was documented and worked 100% today, it's still a lot more work to add a popup to a column header than anywhere else. (Another way to look at it: You can probably implement its 'clicked' signal by using column->button, too, but we don't make programmers do that.) If this is still the plan, just give the word, and I'll file a bunch of bugs to fix it so it's actually usable. Or maybe I'm completely missing something, and it is easy to do and get working; if that's the case, hit me over the head and let me know that, too. Thanks. :-)
Reopening.
The behaviour mentioned in point 2 is because when you pop up a context menu, you perform an X pointer grab. This means that the column header never gets the button release event it is expecting. One fix would be to send a fake button release event to the column header, or to not propagate the button press event after you handle it. You often need to perform this dance when popping up a menu on button press, or anything else that requires an X pointer grab.
Billy: Yeah, that makes sense, but (a) Gtk+ doesn't require this sort of dance for other popup menus (in the content area of a TreeView, for example), and (b) I've never gotten it to work, or seen any program which has. Have you gotten this to work? I'd love to see how. I tried: - emitting button-release-event on tree, column, widget, button - stopping (by_name) button-press-event on tree, column, widget, button - returning TRUE from my button-press-event handler with ("stop other handlers from being invoked for the event") -- I always did this, anyway I couldn't get any of these to work, but it's quite possible I'm missing something. I also asked on #pygtk, and only got some workarounds that changed the behavior (show menu on mouse-up, disallow column dragging, etc.). I can attach a fairly short PyGTK program that demonstrates the problem, if you (or anybody else) want to play with it but don't have such a program handy. (If I learn how to do this, I'll gladly write up some documentation for the manual. :-)
I've fixed it so that it no longer reorders on buttons other than 1. We should probably add a gtk_tree_view_column_get_button() API call, as context menus on the button seems legitimate.
Found this bug after wrestling with the exact same issues as comment #3. A gtk_tree_view_column_get_button call just dumps the guts onto the developer in my opinion, and doesn't correct a fundamental inconsistency. The GtkTreeView is a container, however GtkTreeViewColumn is not a widget you pack inside it but is instead derived from GtkObject. However if you think about it, unlike any of its peer GtkObject classes, the column shows all the signs of being a widget. It's therefore disappointing to notice you can't do a whole lot with it as you're limited to the small number of functions in the GtkTreeViewColumn API that wraps the user visible widget. So while you can control simple things like spacing and visibility, if you want to change the sensitivity, focus behavior, add a tooltip etc. you're out of luck. If it was made a widget it would be a lot simpler from a developer's point of view. For example, you could simply bind a handler to the "popup-menu" signal, which is *not* the same as merely popping up a menu on a right-click event from an accessibility point of view. You could also remove all of the API that simply maps properties to the internal button.
*** Bug 148558 has been marked as a duplicate of this bug. ***
I have also wrestled with this problem. I ended up connecting to the column->button with the button-release-event as Matthias Clasen suggested. You can see my code at: http://cvs.berlios.de/cgi-bin/viewcvs.cgi/linuxdcpp/linuxdcpp/linux/treeview.cc In my opinion, using column->button is not the proper solution. There should be a way to connect to the entire header of a GtkTreeView. Besides the fact that it breaks API, there are number of reasons why connecting to column->button should be avoided. If you just connect to the column headers, then when the gap between the column headers is clicked, the event for the body of the treeview is triggered instead of the desired header-triggered event. Also, I use an extra padding column so that there's a gap between the last column and the end of the treeview. If this area is clicked, the desired event isn't triggered either. This is why it is my belief that a function like gtk_tree_view_get_header() should be implemented so that events can be registered with it to occur when any part of it is clicked. Unfortunately, I couldn't even find a way to get the header by breaking API like I did with column->button, so I don't know how easy this is. It seems to be saved in a GdkWindow, whereas I need it in a form I can connect events to. Obviously, connecting to individual column headers may be appropriate for some cases, so maybe there should be both a way to get the entire header and individual column buttons.
Thanks for taking the time to report this bug. However, you are using a version that is too old and not supported anymore. GNOME developers are no longer working on that version, so unfortunately there will not be any bug fixes for the version that you use. By upgrading to a newer version of GNOME you could receive bug fixes and new functionality. You may need to upgrade your Linux distribution to obtain a newer version of GNOME. Please feel free to reopen this bug if the problem still occurs with a newer version of GNOME.
I believe that this problem still exists.
We're moving to gitlab! As part of this move, we are closing bugs that haven't seen activity in more than 5 years. If this issue is still imporant to you and still relevant with GTK+ 3.22 or master, please consider creating a gitlab issue for it.