Search code examples
cgtkcopy-constructorgobject

Is there a good way to copy a Gtk widget?


Is there a way, using the Gtk library in C, to clone a Gtk button (for instance), and pack it somewhere else in the app. I know you can't pack the same widget twice. And that this code obviously wouldn't work, but shows what happens when I attempt a shallow copy of the button:

GtkButton *a = g_object_new(GTK_TYPE_BUTTON, "label", "o_0", NULL);
GtkButton *b = g_memdup(a, sizeof *a);
gtk_box_pack_start_defaults(GTK_BOX(vbox), GTK_WIDGET(b));

There is surrounding code which creates a vbox and packs it in a window and runs gtk_main(). This will result in these hard to understand error messages:

(main:6044): Gtk-CRITICAL **: gtk_widget_hide: assertion `GTK_IS_WIDGET (widget)' failed

(main:6044): Gtk-CRITICAL **: gtk_widget_realize: assertion `GTK_WIDGET_ANCHORED (widget) || GTK_IS_INVISIBLE (widget)' failed
**
Gtk:ERROR:/build/buildd/gtk+2.0-2.18.3/gtk/gtkwidget.c:8431:gtk_widget_real_map: assertion failed: (GTK_WIDGET_REALIZED (widget))

Along the same lines, if I were to write my own GObject (not necessarily a Gtk widget), is there a good way to write a copy constructor. Im thinking it should be an interface with optional hooks and based mostly on the properties, handling the class's hierarchy in some way.

I'd want to do this:

GtkButton *b = copyable_copy(COPYABLE(a));

If GtkButton could use a theoretical copyable interface.


Solution

  • A clone throught properties is a viable solution:

    GObject *
    g_object_clone(GObject *src)
    {
        GObject *dst;
        GParameter *params;
        GParamSpec **specs;
        guint n, n_specs, n_params;
    
        specs = g_object_class_list_properties(G_OBJECT_GET_CLASS(src), &n_specs);
        params = g_new0(GParameter, n_specs);
        n_params = 0;
    
        for (n = 0; n < n_specs; ++n)
            if (strcmp(specs[n]->name, "parent") &&
                (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
                params[n_params].name = g_intern_string(specs[n]->name);
                g_value_init(&params[n_params].value, specs[n]->value_type);
                g_object_get_property(src, specs[n]->name, &params[n_params].value);
                ++ n_params;
            }
    
        dst = g_object_newv(G_TYPE_FROM_INSTANCE(src), n_params, params);
        g_free(specs);
        g_free(params);
    
        return dst;
    }
    

    Cloning a widget is not that trivial though, but the above approach is usable in most cases (on a GtkButton for sure).

    I'd not care that much of states not exposed with properties (all proper widgets should be fully defined by properties to be usable with GtkBuilder) but a lot of corner cases will make a robust cloning quite difficult (interfaces and containers being the first ones that come to my mind).