I've made a simple custom widget for GTK+3 in C, named AwLed, and added it to a custom Glade catalog. It works as expected inside Glade or when created using the aw_led_new() function. However, when I make a simple program that loads the Glade file and shows its widgets (calling the show_all() on the main window widget), the custom widgets don't show themselves. I added some printf-fflushs to debug and found out that the functions Realize, Allocate aren't even called. It would seem like the whole shared library is not loaded.
Now, what Glade does other than loading the widgets with GtkBuilder? Why these widgets are ignored? Am I suppose to write some boilerplate to implement the GtkBuildable interface?
Any help is well appreciated.
awled.c
#include <gtk/gtk.h>
#include "awled.h"
enum {
PROP_0,
PROP_ACTIVE,
};
static void aw_led_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void aw_led_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void aw_led_realize (GtkWidget *widget);
static void aw_led_unrealize (GtkWidget *widget);
static void aw_led_map (GtkWidget *widget);
static void aw_led_unmap (GtkWidget *widget);
static void aw_led_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void aw_led_get_preferred_width (GtkWidget *widget,
gint *minimal_width,
gint *natural_width);
static void aw_led_get_preferred_height (GtkWidget *widget,
gint *minimal_height,
gint *natural_height);
static gboolean aw_led_draw (GtkWidget *widget,
cairo_t *cr);
G_DEFINE_TYPE (AwLed, aw_led, GTK_TYPE_WIDGET)
#define parent_class aw_led_parent_class
static void
aw_led_class_init (AwLedClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->set_property = aw_led_set_property;
object_class->get_property = aw_led_get_property;
widget_class->realize = aw_led_realize;
widget_class->unrealize = aw_led_unrealize;
widget_class->map = aw_led_map;
widget_class->unmap = aw_led_unmap;
widget_class->size_allocate = aw_led_size_allocate;
widget_class->get_preferred_width = aw_led_get_preferred_width;
widget_class->get_preferred_height = aw_led_get_preferred_height;
widget_class->draw = aw_led_draw;
GParamSpec* param_spec;
param_spec = g_param_spec_boolean ("active", "Active", "LED State",
FALSE, G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_ACTIVE, param_spec);
}
static void
aw_led_init (AwLed *led)
{
gtk_widget_set_has_window (GTK_WIDGET (led), FALSE);
led->state = FALSE;
led->color_on.red = 0.0;
led->color_on.green = 0.6;
led->color_on.blue = 0.0;
led->color_on.alpha = 1.0;
led->color_off.red = 0.6;
led->color_off.green = 0.0;
led->color_off.blue = 0.0;
led->color_off.alpha = 1.0;
/*priv->backing_store = NULL; //what?? needed?
priv->backing_store_valid = FALSE;
priv->pos_redraw_idle_id = 0;*/
}
static void
aw_led_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
AwLed *led = AW_LED (object);
switch (prop_id)
{
case PROP_ACTIVE:
led->state = g_value_get_boolean (value);
gtk_widget_queue_resize (GTK_WIDGET (led));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
aw_led_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
AwLed *led = AW_LED (object);
switch (prop_id)
{
case PROP_ACTIVE:
g_value_set_boolean (value, led->state);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
GtkWidget *
aw_led_new ()
{
return g_object_new (AW_TYPE_LED,
"active", FALSE,
NULL);
}
static void
aw_led_realize (GtkWidget *widget)
{
AwLed *led = AW_LED (widget);
GtkAllocation allocation;
GdkWindowAttr attributes;
gint attributes_mask;
GTK_WIDGET_CLASS (aw_led_parent_class)->realize (widget);
gtk_widget_get_allocation (widget, &allocation);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask = (gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK);
attributes_mask = GDK_WA_X | GDK_WA_Y;
led->input_window = gdk_window_new (gtk_widget_get_window (widget),
&attributes, attributes_mask);
gdk_window_set_user_data (led->input_window, led);
}
static void
aw_led_unrealize (GtkWidget *widget)
{
AwLed *led = AW_LED (widget);
if (led->input_window) {
gdk_window_destroy (led->input_window);
led->input_window = NULL;
}
GTK_WIDGET_CLASS (aw_led_parent_class)->unrealize (widget);
}
static void
aw_led_map (GtkWidget *widget)
{
AwLed *led = AW_LED (widget);
GTK_WIDGET_CLASS (parent_class)->map (widget);
if (led->input_window)
gdk_window_show (led->input_window);
}
static void
aw_led_unmap (GtkWidget *widget)
{
AwLed *led = AW_LED (widget);
if (led->input_window)
gdk_window_hide (led->input_window);
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
}
static void
aw_led_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
AwLed *led = AW_LED (widget);
gtk_widget_set_allocation (widget, allocation);
if (gtk_widget_get_realized (widget)) {
gdk_window_move_resize (led->input_window,
allocation->x, allocation->y,
allocation->width, allocation->height);
}
}
static void
aw_led_get_preferred_width (GtkWidget *widget,
gint *minimal_width,
gint *natural_width)
{
*minimal_width = 20;
*natural_width = 35;
}
static void
aw_led_get_preferred_height (GtkWidget *widget,
gint *minimal_height,
gint *natural_height)
{
*minimal_height = 20;
*natural_height = 35;
}
static gboolean
aw_led_draw (GtkWidget *widget,
cairo_t *cr)
{
gint width, height;
AwLed* led = AW_LED(widget);
width = gtk_widget_get_allocated_width (widget);
height = gtk_widget_get_allocated_height (widget);
GdkRGBA color = (led->state) ? led->color_on : led->color_off;
cairo_arc (cr, width / 2.0, height / 2.0,
MIN (width, height) / 2.0, 0, 2 * G_PI);
gdk_cairo_set_source_rgba (cr, &color);
cairo_fill (cr);
return TRUE;
}
awled.h
#ifndef __AW_LED_H__
#define __AW_LED_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define AW_TYPE_LED (aw_led_get_type ())
#define AW_LED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), AW_TYPE_LED, AwLed))
#define AW_LED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AW_TYPE_LED, AwLedClass))
#define AW_IS_LED(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AW_TYPE_LED))
#define AW_IS_LED_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), AW_TYPE_LED))
#define AW_LED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), AW_TYPE_LED, AwLedClass))
typedef struct _AwLed AwLed;
typedef struct _AwLedClass AwLedClass;
typedef enum {
OVAL,
RECTANGULAR
} AwLedShape;
struct _AwLed {
GtkWidget parent_instance;
//public members
//@TODO Move these to .C file (AwLedPrivate structure)
GdkRGBA color_on;
GdkRGBA color_off;
AwLedShape shape;
gboolean state;
GdkWindow *input_window;
};
struct _AwLedClass {
GtkWidgetClass parent_class;
};
GType aw_led_get_type (void) G_GNUC_CONST;
GtkWidget* aw_led_new ();
gboolean aw_led_get_state (AwLed* led);
void aw_led_set_state (AwLed* led, gboolean state);
G_END_DECLS
#endif /* __AW_LED_H__ */
test_builder.c
#include <gtk/gtk.h>
#include <awled.h>
int main(int argc, char* argv[]) {
GtkBuilder* builder;
GtkWidget *window, *led, *led2, *box;
gtk_init(&argc, &argv);
builder = gtk_builder_new();
gtk_builder_add_from_file(builder, "interface.ui", NULL);
window = (GtkWidget*) gtk_builder_get_object(builder, "window1");
led = (GtkWidget*) gtk_builder_get_object(builder, "led1");
box = (GtkWidget*) gtk_builder_get_object(builder, "box1");
//weird... if I create only one this way, all of them show themselves!
//led2 = aw_led_new();
//gtk_box_pack_end(GTK_BOX(box), led2, 0, 0, 0);
gtk_widget_show_all(window);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
}