GNOME Bugzilla – Bug 774778
gtk+4, gtkwidget.c: get_render_mode() is problematic for gtkmm
Last modified: 2017-02-15 10:20:05 UTC
Gtk+ now has two ways of rendering a widget. The local get_render_mode() determines which mode to use for a specific widget. It is not compatible with the way gtkmm overrides virtual functions and default signal handlers. An example, to show what gtkmm does: Let's look at a fictitious widget, called GtkAbc. It's wrapped in C++ code in gtkmm. Gtkmm then registers a class, gtkmm__GtkAbc, derived from GtkAbc. In its class init() function, it overrides all vfuncs and default signal handlers which shall be available to C++ application programmers. That has so far included GtkWidget::draw. In the future I suppose it will also include the GtkWidget::snapshot vfunc. Most of the overridden signal handlers and vfuncs do nothing. When they are called, they just chain the call up to the base class, GtkAbc. So what are these overridden functions good for? A C++ programmer, using the C++ class Gtk::Abc (as it's called in C++ code), can derive from class Gtk::Abc, making e.g. class MyAbc : public Gtk::Abc With very few exceptions, such a derived class is not registered in the GType system. To glib and gtk+, instances of both MyAbc and Gtk::Abc look like instances of gtkmm__GtkAbc. Signal handlers and vfuncs can be overridden (in a C++ way) in MyAbc. Some instances of gtkmm__GtkAbc may chain a call to ::draw up to GtkAbc, while other instances may do the drawing in the derived class. What will happen is determined when the signal handler or vfunc is called. Summary: get_render_mode() determines which render mode to use by looking at which signal handlers and vfuncs are overridden. Gtkmm overrides almost everything and determines what to do when a signal is emitted or a vfunc is called. What can we do? I'm not sure. Perhaps add a gtk_widget_set_render_mode() that can be called by every widget? Not very attractive, I admit. If you do add such a function, please don't require that it must be called in the class init() or instance init() function. C++ programmers using gtkmm can't add anything to those functions. It ought to be enough that a set_render_mode() function is called before the widget is rendered. And it must be set per instance, not just per GType class. An unrelated comment: gtkwidget.h says "@snapshot: Signal emitted ...". But snapshot is not a signal, it's a vfunc.
s/Most of the overridden signal handlers/Most of the overriding signal handlers/ s/these overridden functions good for?/these overriding functions good for?/
> such a derived class is not registered in the GType system. Now I'm left wondering how *anything* in GTKmm even works. Subclassing without notifying the GType type system is a surefire way to break something. GObject does not know anything about C++ types, if those types are not shared with the underlying type system. In any case, the real problem is: > get_render_mode() determines which render mode to use by > looking at which signal handlers and vfuncs are overridden. which is the result, as you say, of C++ overriding everything up front. Sadly, we don't have any other way to determine if something is effectively overriding things at the C level, which is needed to determine whether or not we can call into compatibility API. Adding a gtk_widget_class_set_render_mode() may work, since it's a per-class issue, not a per-instance once; but few language bindings even support the ability to get to the GTypeClass pointer, and, as you say, it's still a non-starter for GTKmm: > If you do add such a function, please don't require that it must > be called in the class init() or instance init() function. I'm sorry, but calling random GObject/GTK API into random places does not guarantee any plausible behaviour that the toolkit itself can rely on. We've already been here with the C++ bindings adding random interfaces and properties to types long after the GType class init has been called.
Created attachment 340522 [details] [review] patch: widget: Make get_render_mode() virtual Is this patch unacceptable or only disgusting? It would work with gtkmm. I understand that it looks strange. Here's how it works: 1. For a gtk+ widget such as GtkAbc the result is just like calling the present non-virtual get_render_mode(). 2. For a gtkmm widget such as gtkmm__GtkAbc, get_render_mode() is overridden in gtkmm__GtkAbc. Gtkmm tries to figure out what mode to use. If code in gtkmm or user code that derives from a gtkmm widget will render the widget, GTK_RENDER_DRAW or GTK_RENDER_SNAPSHOT is returned. If, as is usually the case, it's left to gtk+ to do the rendering, gtkmm__GtkAbc.get_render_mode() chains up to GtkAbc.get_render_mode() with GtkWidgetClass* klass set to the class of GtkAbc. Even if you accept such a strange solution, the patch needs some refinement before it's pushed. E.g. some documentation and a better commit message. > Now I'm left wondering how *anything* in GTKmm even works. Usually it works well. > Subclassing without notifying the GType type system is a surefire way to break > something. It makes subclassing in C++ code much much easier. > GObject does not know anything about C++ types, if those types are not shared > with the underlying type system. Often it need not know. At least up until now. > We've already been here with the C++ bindings adding random interfaces and > properties to types long after the GType class init has been called. At long last we managed to add interfaces (but not properties) in the class init() function. When restrictions are added after several years, it's no wonder it can cause problems somewhere.
I've implemented a reasonable way of handling this problem in gtkmm. No need for a fix in gtk+. See gtkmm bug 775348.