How to add a GtkWidget in a custom one?


I made a custom GTK widget and I need to add a GTK Button to it so to inherit its signals in the custom one. The custom widget is a timeline so I thought to use GTK Buttons to avoid messing with mouse coordinates check. When the user wants to move the picture represented by the button or just extend its delay on the timeline itself, signals will be emitted from the button widget and my life will be much easier. My custom widget inherits from GtkDrawingArea. I tried to use a GtkLayout instead but with no luck.

The button of course is not shown on the timeline. that’s my problem. I know I have to put the button inside a container but how to do it in the draw function? Does anyone have a clue on how to do it? I googled and looked here but with no luck. I know it’s possibile as gtk_textview can add a child widget into it. I looked at the source code but I couldn’t understand how it achieves this.

This is the custom widget draw function:

static gboolean img_timeline_draw(GtkWidget *da, cairo_t *cr)
  GtkWidget *button; 
  ImgTimelinePrivate *priv = img_timeline_get_instance_private((ImgTimeline*)da);
  gint width = gtk_widget_get_allocated_width(da);
  cairo_translate (cr, 0 , 12);

  img_timeline_draw_time_ticks(da, cr, width);
  //Video timeline
  cairo_translate (cr, 0 , 20);
  cairo_set_source_rgba(cr, priv->video_background[0], priv->video_background[1], priv->video_background[2], priv->video_background[3]);
  cairo_set_line_width(cr, 1);
  cairo_rectangle(cr, 0,10, width - 2, 59);
  cairo_set_source_rgb(cr, 1, 1, 1);
  cairo_rectangle(cr, 0,69, width - 2, 3);
  //Audio timeline
  cairo_set_source_rgba(cr, priv->audio_background[0], priv->audio_background[1], priv->audio_background[2], priv->audio_background[3]);
  cairo_rectangle(cr, 0,72, width - 2, 59);

  button = gtk_button_new_with_label("I'm invisible...");
  return FALSE;

And this is the main.c:

int main(int argc, char *argv[])
  gtk_init(&argc, &argv);

  GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "Imagination timeline widget");
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 800, 200);
  g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

  GtkWidget *timeline = img_timeline_new();
  g_object_set(timeline, "total_time",        300, NULL);
  g_object_set(timeline, "video_background", "#0084ff", NULL);
  g_object_set(timeline, "audio_background", "#0084ff", NULL);

  gtk_widget_add_events( timeline, GDK_BUTTON1_MOTION_MASK
                         | GDK_POINTER_MOTION_MASK
                         | GDK_BUTTON_PRESS_MASK
                         | GDK_BUTTON_RELEASE_MASK
                         | GDK_SCROLL_MASK
                         | GDK_KEY_PRESS_MASK );

  GtkWidget *scrolledwindow1 = gtk_scrolled_window_new(NULL, NULL);
  GtkWidget *viewport = gtk_viewport_new(NULL,NULL);
  g_signal_connect( G_OBJECT(timeline), "scroll-event", G_CALLBACK(img_timeline_scroll), scrolledwindow1);

  gtk_widget_set_hexpand(viewport, TRUE);
  gtk_widget_set_vexpand(viewport, TRUE);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow1), GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
  gtk_container_add (GTK_CONTAINER(viewport), timeline);
  gtk_container_add (GTK_CONTAINER(scrolledwindow1), viewport);
  gtk_container_add (GTK_CONTAINER (window), scrolledwindow1);

  return 0;

Would anyone please help? Or point me to some software which did this so I can look the source code?

Many thanks

You can’t add a widget in the draw function. You add them to your container, and then the draw() function will recurse into each child widget. You custom widget needs to derive from GtkContainer, and you need to chain up to the parent’s implementation.

You should start reading the documentation on the wiki.

1 Like

Thank you so much INDEED for replying Emmanuele, I do appreciate. I have derived from GtkLayout which gives me a drawing area too. Would you please explain what do you mean with:

" the draw() function will recurse into each child widget."?

Do you mean I have to call gtk_widget_show() on each of the child widgets?

I read the link you gave me.

First of all: you need to call show() on any child widget you want to be visible; but that doesn’t mean you can add widgets inside the draw() call.

Let me be clear: you cannot add widgets inside a draw() implementation.

You need to add children to a container when constructing the UI.

The default draw() implementation of the GtkContainer class is going to recurse through all the children of the container, and draw each visible child.

What you’re trying to achieve seems definitely above your level of GTK knowledge; you should familiarise yourself with how GTK works before you venture into writing complex widgets from scratch.

1 Like

Yeah I got that, it’s clear, thanks. Which material do you suggest me to read apart the documentation? Or can you suggest a specific part of it I’ve been missing? Thanks :pray:

The API reference should contain all that’s needed; the “How Do I” pages contain ancillary information. You can also find guides on

Additionally, there are a lot of free and open source software applications you can study.

Thanks I will consult the two links you gave me. Regarding the “a lot of free and open source software” I was unable to find it. You suggested “Pitivi” in another thread but It’s in Python. Do you know any other software in C I can study? Anyone with custom widgets? Thank you so much for your time. I do appreciate it, Emmanuele. :pray:

I don’t know of C projects with timeline widgets. Custom widgets are pretty much everywhere.

How is this done if my draw function uses cairo calls to paint the time ticks of the timeline without taking care of any child widgets?

By the way I used both gtk_container_add() or gtk_layout_put() but the button is not shown and it doesn’t appear in the list of widgets inside the GTK INSPECTOR. Do you have a clue?

By chaining up to the parent’s implementation of the draw() virtual function from your own.

Or, if you want to override it completely, by calling gtk_container_propagate_draw().

I read this from the documentation about gtk_container_propagate_draw():

“When a container receives a call to the draw function, it must send synthetic draw calls to all children that don’t have their own [GdkWindows”.

The button I added does have a GdkWindow because it receives events. Am I right, Emmanuele?

So I conclude I don’t have to use gtk_container_propagate_draw() to let the widgets I add to my custom one to show up. By the way I used this function and it still doesn’t show up sadly.

No, you are not. Buttons use an input window, which is not used for drawing.

I literally have no idea of what you’re trying to achieve. Your code doesn’t make it clear at all.

If you’re subclassing GtkLayout and overriding the draw() virtual function to draw your own background, you should do:

static gboolean
your_widget_draw (GtkWidget *widget, cairo_t *cr)
  // draw your background
  // …

  // chain up to the parent implementation for drawing the children:
  GTK_WIDGET_CLASS (your_widget_parent_class)->draw (widget, cr);

  return TRUE;

If that doesn’t work, then you’re doing something else wrong.

Again: you’re jumping off into the very deep end, and it seems you’re not really knowledgeable in how GTK works. I recommend reading some more code before you attempt deriving/implementing complex widgets.

Ok, I was finally able to have the button showing up in the timeline thanks to you so much indeed for pointing me to gtk_container_propagate_draw(). I have now a weird problem, I added a screenshot to explain it. Do you know why when I click on the famous button only half of it gets the focus? The arrow is not shown in the screenshot but it’s visible. Where am I wrong in the creation of the button???

Here is the code:

And this is the draw function:

Ok, I added cairo_save(cr); and cairo_restore(cr) and I fixed the problem! Hip hip hurrĂ ! Thank you so much again @ebassi! I will make treasure of your suggestions!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.