Éléments graphiques personnalisés
gtkmm facilite la dérivation de nouveaux éléments graphiques par héritage à partir d'une classe d'élément graphique existante, soit par dérivation à partir d'un conteneur et en y ajoutant des éléments graphiques enfants, soit par dérivation d'un élément graphique simple en modifiant son comportement. Mais vous pouvez parfois trouver qu'il n'y a aucun point de départ convenable. Dans ce cas, vous pouvez implémenter un élément graphique de A à Z.

Conteneurs personnalisés
When deriving from Gtk::Container, you should override the following virtual methods:
get_request_mode_vfunc(): Return what Gtk::SizeRequestMode is preferred by the container.
get_preferred_width_vfunc(): Calculate the minimum and natural width of the container.
get_preferred_height_vfunc(): Calculate the minimum and natural height of the container.
get_preferred_width_for_height_vfunc(): Calculate the minimum and natural width of the container, if it would be given the specified height.
get_preferred_height_for_width_vfunc(): Calculate the minimum and natural height of the container, if it would be given the specified width.
on_size_allocate(): Position the child widgets, given the height and width that the container has actually been given.
forall_vfunc(): Call the same callback for each of the children.
on_add(): Add a child widget to the container.
on_remove(): Remove a child widget from the container.
child_type_vfunc(): Return what type of child can be added.
Les fonctions membres virtuelles get_request_mode_vfunc(), get_preferred_width_vfunc(), get_preferred_height_vfunc(), get_preferred_width_for_height_vfunc(), get_preferred_height_for_width_vfunc() et on_size_allocate() contrôlent la disposition des éléments graphiques enfants. Par exemple, si votre conteneur possède deux éléments graphiques enfants, l'un au dessus de l'autre, votre fonction membre get_preferred_width_vfunc() doit renvoyer la plus grande des largeurs des éléments graphiques enfants et get_preferred_height_for_width_vfunc() la somme des hauteurs. Si vous voulez de l'espace libre autour les éléments graphiques enfants, vous devez également l'additionner à la largeur et à la hauteur. Votre conteneur d'éléments graphiques se sert de ces résultats pour s'assurer que vos éléments graphiques ont assez de place. En examinant chaque parent de l'élément graphique, et les parents de celui-ci, cette logique détermine éventuellement la taille de la fenêtre de plus haut niveau.
Vous n'avez pas la garantie d'obtenir le Gtk::SizeRequestMode demandé. C'est pourquoi les quatre fonctions membres get_preferred_xxx_vfunc() doivent renvoyer des valeurs raisonnables.
on_size_allocate(), toutefois, reçoit les vraies hauteur et largeur que le conteneur parent a décidé de donner à l'élément graphique. Cela peut être plus que le minimum ou même plus que la taille naturelle, par exemple, si la fenêtre de haut niveau a été agrandie. Vous pouvez choisir d'ignorer l'espace supplémentaire et laisser la zone inoccupée, ou bien vous pouvez choisir d'agrandir vos éléments graphiques enfants pour remplir cet espace, ou bien encore vous pouvez choisir d'augmenter l'espace libre entre vos éléments graphiques. C'est votre conteneur, à vous de décider. N'oubliez pas d'appeler set_allocation() à l'intérieur de votre implémentation de on_size_allocate() pour utiliser effectivement l'espace alloué offert par le conteneur parent.
À moins que votre conteneur ne soit une fenêtre de haut niveau dérivée de Gtk::Window, vous devez également appeler Gtk::Widget::set_has_window(false) dans le constructeur. Cela signifie que votre conteneur ne crée pas sa propre Gdk::Window, mais utilise la fenêtre du parent. (Notez la différence entre Gtk::Window et Gdk::Window.) Si votre conteneur n'a pas besoin de sa propre Gdk::Window et ne dérive pas d'une Gtk::Window, vous devez aussi surdéfinir la fonction membre on_realize() comme cela est décrit dans la section Éléments graphiques personnalisés. Et, sauf si votre conteneur dessine directement dans la Gdk::Window sous-jacente, vous devrez probablement appeler set_redraw_on_allocate(false) pour améliorer les performances.
En surdéfinissant forall_vfunc() vous permettez aux applications d'opérer sur tous les éléments graphiques enfants du conteneur. Par exemple, show_all_children() utilise cette possibilité pour trouver tous les éléments graphiques enfants et les afficher.
Même si votre conteneur possède sa propre fonction membre pour définir ses éléments graphiques enfants, vous devez quand même fournir une implémentation des fonctions membres virtuelles on_add() et on_remove() de la classe de base de façon à ce que les fonctions membres add() et remove() fassent quelque chose d'approprié si elles sont appelées.
Votre implémentation de la fonction membre child_type_vfunc() doit renvoyer le type d'élément graphique pouvant être ajouté à votre conteneur s'il n'est pas encore rempli. Généralement, c'est à Gtk::Widget::get_type() d'indiquer quelles sont les classes dérivées de Gtk::Widget acceptées. Si le conteneur ne peut plus contenir d'autres éléments graphiques, cette fonction membre doit renvoyer la valeur G_TYPE_NONE.

Exemple
This example implements a container with two child widgets, one above the other. Of course, in this case it would be far simpler just to use a vertical Gtk::Box.

Conteneur personnalisé
Source Code
File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>
#include "mycontainer.h"

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_quit();

  //Child widgets:
  Gtk::Box m_VBox;
  MyContainer m_MyContainer;
  Gtk::Button m_Button_One;
  Gtk::Label m_Label_Two;
  Gtk::ButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: mycontainer.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H
#define GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H

#include <gtkmm/container.h>

class MyContainer : public Gtk::Container
{
public:
  MyContainer();
  virtual ~MyContainer();

  void set_child_widgets(Gtk::Widget& child_one, Gtk::Widget& child_two);

protected:

  //Overrides:
  virtual Gtk::SizeRequestMode get_request_mode_vfunc() const;
  virtual void get_preferred_width_vfunc(int& minimum_width, int& natural_width) const;
  virtual void get_preferred_height_for_width_vfunc(int width, int& minimum_height, int& natural_height) const;
  virtual void get_preferred_height_vfunc(int& minimum_height, int& natural_height) const;
  virtual void get_preferred_width_for_height_vfunc(int height, int& minimum_width, int& natural_width) const;
  virtual void on_size_allocate(Gtk::Allocation& allocation);

  virtual void forall_vfunc(gboolean include_internals, GtkCallback callback, gpointer callback_data);

  virtual void on_add(Gtk::Widget* child);
  virtual void on_remove(Gtk::Widget* child);
  virtual GType child_type_vfunc() const;

  Gtk::Widget* m_child_one;
  Gtk::Widget* m_child_two;
};

#endif //GTKMM_CUSTOM_CONTAINER_MYCONTAINER_H

File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)

#include <iostream>
#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Button_One("Child One"),
  m_Label_Two("Child 2"),
  m_Button_Quit("Quit")
{
  set_title("Custom Container example");
  set_border_width(6);
  set_default_size(400, 200);

  add(m_VBox);

  //Add the child widgets to the custom container:
  m_MyContainer.set_child_widgets(m_Button_One, m_Label_Two);

  m_Label_Two.set_alignment(1.0, 0.5);

  m_VBox.pack_start(m_MyContainer, Gtk::PACK_EXPAND_WIDGET);
  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_ButtonBox.set_border_width(6);
  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
  m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit) );

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char *argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

File: mycontainer.cc (For use with gtkmm 3, not gtkmm 2)

#include <iostream>
#include <algorithm> // std::max
#include "mycontainer.h"

MyContainer::MyContainer()
: m_child_one(0), m_child_two(0)
{
  set_has_window(false);
  set_redraw_on_allocate(false);
}

MyContainer::~MyContainer()
{
/*
  // These calls to Gtk::Widget::unparent() are necessary if MyContainer is
  // deleted before its children. But if you use a version of gtkmm where bug
  // https://bugzilla.gnome.org/show_bug.cgi?id=605728
  // has not been fixed (gtkmm 3.7.10 or earlier) and the children are deleted
  // before the container, these calls can make the program crash.
  // That's because on_remove() is not called, when the children are deleted.
  if (m_child_one)
    m_child_one->unparent();

  if (m_child_two)
    m_child_two->unparent();
*/
}

void MyContainer::set_child_widgets(Gtk::Widget& child_one,
        Gtk::Widget& child_two)
{
  m_child_one = &child_one;
  m_child_two = &child_two;

  m_child_one->set_parent(*this);
  m_child_two->set_parent(*this);
}

//This example container is a simplified VBox with at most two children.
Gtk::SizeRequestMode MyContainer::get_request_mode_vfunc() const
{
  return Gtk::SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}

//Discover the total amount of minimum space and natural space needed by
//this container and its children.
void MyContainer::get_preferred_width_vfunc(int& minimum_width, int& natural_width) const
{
  int child_minimum_width[2] = {0, 0};
  int child_natural_width[2] = {0, 0};

  if(m_child_one && m_child_one->get_visible())
    m_child_one->get_preferred_width(child_minimum_width[0], child_natural_width[0]);

  if(m_child_two && m_child_two->get_visible())
    m_child_two->get_preferred_width(child_minimum_width[1], child_natural_width[1]);

  //Request a width equal to the width of the widest visible child.
  minimum_width = std::max(child_minimum_width[0], child_minimum_width[1]);
  natural_width = std::max(child_natural_width[0], child_natural_width[1]);
}

void MyContainer::get_preferred_height_for_width_vfunc(int width,
   int& minimum_height, int& natural_height) const
{
  int child_minimum_height[2] = {0, 0};
  int child_natural_height[2] = {0, 0};
  int nvis_children = 0;

  if(m_child_one && m_child_one->get_visible())
  {
    ++nvis_children;
    m_child_one->get_preferred_height_for_width(width, child_minimum_height[0],
                                                child_natural_height[0]);
  }

  if(m_child_two && m_child_two->get_visible())
  {
    ++nvis_children;
    m_child_two->get_preferred_height_for_width(width, child_minimum_height[1],
                                                child_natural_height[1]);
  }

  //The allocated height will be divided equally among the visible children.
  //Request a height equal to the number of visible children times the height
  //of the highest child.
  minimum_height = nvis_children * std::max(child_minimum_height[0],
                                            child_minimum_height[1]);
  natural_height = nvis_children * std::max(child_natural_height[0],
                                            child_natural_height[1]);
}

void MyContainer::get_preferred_height_vfunc(int& minimum_height, int& natural_height) const
{
  int child_minimum_height[2] = {0, 0};
  int child_natural_height[2] = {0, 0};
  int nvis_children = 0;

  if(m_child_one && m_child_one->get_visible())
  {
    ++nvis_children;
    m_child_one->get_preferred_height(child_minimum_height[0], child_natural_height[0]);
  }

  if(m_child_two && m_child_two->get_visible())
  {
    ++nvis_children;
    m_child_two->get_preferred_height(child_minimum_height[1], child_natural_height[1]);
  }

  //The allocated height will be divided equally among the visible children.
  //Request a height equal to the number of visible children times the height
  //of the highest child.
  minimum_height = nvis_children * std::max(child_minimum_height[0],
                                            child_minimum_height[1]);
  natural_height = nvis_children * std::max(child_natural_height[0],
                                            child_natural_height[1]);
}

void MyContainer::get_preferred_width_for_height_vfunc(int height,
   int& minimum_width, int& natural_width) const
{
  int child_minimum_width[2] = {0, 0};
  int child_natural_width[2] = {0, 0};
  int nvis_children = 0;

  //Get number of visible children.
  if(m_child_one && m_child_one->get_visible())
    ++nvis_children;
  if(m_child_two && m_child_two->get_visible())
    ++nvis_children;

  if(nvis_children > 0)
  {
    //Divide the height equally among the visible children.
    const int height_per_child = height / nvis_children;

    if(m_child_one && m_child_one->get_visible())
      m_child_one->get_preferred_width_for_height(height_per_child,
                   child_minimum_width[0], child_natural_width[0]);

    if(m_child_two && m_child_two->get_visible())
      m_child_two->get_preferred_width_for_height(height_per_child,
                   child_minimum_width[1], child_natural_width[1]);
  }

  //Request a width equal to the width of the widest child.
  minimum_width = std::max(child_minimum_width[0], child_minimum_width[1]);
  natural_width = std::max(child_natural_width[0], child_natural_width[1]);
}

void MyContainer::on_size_allocate(Gtk::Allocation& allocation)
{
  //Do something with the space that we have actually been given:
  //(We will not be given heights or widths less than we have requested, though
  //we might get more.)

  //Use the offered allocation for this container:
  set_allocation(allocation);

  //Get number of visible children.
  int nvis_children = 0;
  if(m_child_one && m_child_one->get_visible())
    ++nvis_children;
  if(m_child_two && m_child_two->get_visible())
    ++nvis_children;

  if(nvis_children <= 0)
    return;

  //Assign space to the children:
  Gtk::Allocation child_allocation_one;
  Gtk::Allocation child_allocation_two;

  //Place the first child at the top-left:
  child_allocation_one.set_x( allocation.get_x() );
  child_allocation_one.set_y( allocation.get_y() );

  //Make it take up the full width available:
  child_allocation_one.set_width( allocation.get_width() );

  if(m_child_one && m_child_one->get_visible())
  {
    //Divide the height equally among the visible children.
    child_allocation_one.set_height( allocation.get_height() / nvis_children);
    m_child_one->size_allocate(child_allocation_one);
  }
  else
    child_allocation_one.set_height(0);

  //Place the second child below the first child:
  child_allocation_two.set_x( allocation.get_x() );
  child_allocation_two.set_y( allocation.get_y() +
          child_allocation_one.get_height());

  //Make it take up the full width available:
  child_allocation_two.set_width( allocation.get_width() );

  //Make it take up the remaining height:
  child_allocation_two.set_height( allocation.get_height() -
          child_allocation_one.get_height());

  if(m_child_two && m_child_two->get_visible())
    m_child_two->size_allocate(child_allocation_two);
}

void MyContainer::forall_vfunc(gboolean, GtkCallback callback, gpointer callback_data)
{
  if(m_child_one)
    callback(m_child_one->gobj(), callback_data);

  if(m_child_two)
    callback(m_child_two->gobj(), callback_data);
}

void MyContainer::on_add(Gtk::Widget* child)
{
  if(!m_child_one)
  {
    m_child_one = child;
    m_child_one->set_parent(*this);
  }
  else if(!m_child_two)
  {
    m_child_two = child;
    m_child_two->set_parent(*this);
  }
}

void MyContainer::on_remove(Gtk::Widget* child)
{
  if(child)
  {
    const bool visible = child->get_visible();
    bool found = false;

    if(child == m_child_one)
    {
      m_child_one = 0;
      found = true;
    }
    else if(child == m_child_two)
    {
      m_child_two = 0;
      found = true;
    }

    if(found)
    {
      child->unparent();

      if(visible)
        queue_resize();
    }
  }
}

GType MyContainer::child_type_vfunc() const
{
  //If there is still space for one widget, then report the type of widget that
  //may be added.
  if(!m_child_one || !m_child_two)
    return Gtk::Widget::get_type();
  else
  {
    //No more widgets may be added.
    return G_TYPE_NONE;
  }
}

Éléments graphiques personnalisés
En dérivant directement de la classe Gtk::Widget, vous pouvez faire tous les dessins voulus directement dans votre élément graphique plutôt que seulement placer des éléments graphiques enfants. Par exemple, un objet Gtk::Label écrit directement le texte de l'étiquette sans passer par l'intermédiaire d'autres éléments graphiques.
When deriving from Gtk::Widget, you should override the following virtual methods. The methods marked (optional) need not be overridden in all custom widgets. The base class's methods may be appropriate.
get_request_mode_vfunc(): (optional) Return what Gtk::SizeRequestMode is preferred by the widget.
get_preferred_width_vfunc(): Calculate the minimum and natural width of the widget.
get_preferred_height_vfunc(): Calculate the minimum and natural height of the widget.
get_preferred_width_for_height_vfunc(): Calculate the minimum and natural width of the widget, if it would be given the specified height.
get_preferred_height_for_width_vfunc(): Calculate the minimum and natural height of the widget, if it would be given the specified width.
on_size_allocate(): Position the widget, given the height and width that it has actually been given.
on_realize(): Associate a Gdk::Window with the widget.
on_unrealize(): (optional) Break the association with the Gdk::Window.
on_map(): (optional)
on_unmap(): (optional)
on_draw(): Draw on the supplied Cairo::Context.
Les six premières fonctions membres dans le tableau précédent sont également surdéfinies dans les conteneurs personnalisés. Elles sont brièvement décrites dans la section Conteneurs personnalisés.
La plupart des éléments graphiques personnalisés nécessitent leur propre Gdk::Window pour tracer dessus. Alors vous pouvez appeler Gtk::Widget::set_has_window(true) dans votre constructeur (c'est la valeur par défaut). Si vous n'appelez pas set_has_window(false), vous devez surdéfinir on_realize() et appeler à partir de là Gtk::Widget::set_realized() et Gtk::Widget::set_window().

Exemple
Cet exemple implémente un élément graphique qui trace un triangle de Penrose.

Élément graphique personnalisé
Source Code
File: mywidget.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_CUSTOM_WIDGET_MYWIDGET_H
#define GTKMM_CUSTOM_WIDGET_MYWIDGET_H

#include <gtkmm/widget.h>
#include <gtkmm/cssprovider.h>

class MyWidget : public Gtk::Widget
{
public:
  MyWidget();
  virtual ~MyWidget();

protected:

  //Overrides:
  virtual Gtk::SizeRequestMode get_request_mode_vfunc() const;
  virtual void get_preferred_width_vfunc(int& minimum_width, int& natural_width) const;
  virtual void get_preferred_height_for_width_vfunc(int width, int& minimum_height, int& natural_height) const;
  virtual void get_preferred_height_vfunc(int& minimum_height, int& natural_height) const;
  virtual void get_preferred_width_for_height_vfunc(int height, int& minimum_width, int& natural_width) const;
  virtual void on_size_allocate(Gtk::Allocation& allocation);
  virtual void on_map();
  virtual void on_unmap();
  virtual void on_realize();
  virtual void on_unrealize();
  virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr);

  Glib::RefPtr<Gdk::Window> m_refGdkWindow;
  Glib::RefPtr<Gtk::CssProvider> m_refStyleProvider;

  int m_scale;
};

#endif //GTKMM_CUSTOM_WIDGET_MYWIDGET_H

File: examplewindow.h (For use with gtkmm 3, not gtkmm 2)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>
#include "mywidget.h"

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  virtual ~ExampleWindow();

protected:
  //Signal handlers:
  void on_button_quit();

  //Child widgets:
  Gtk::Box m_VBox;
  MyWidget m_MyWidget;
  Gtk::ButtonBox m_ButtonBox;
  Gtk::Button m_Button_Quit;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: examplewindow.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::ORIENTATION_VERTICAL),
  m_Button_Quit("Quit")
{
  set_title("Custom Widget example");
  set_border_width(6);
  set_default_size(400, 200);

  add(m_VBox);
  m_VBox.pack_start(m_MyWidget, Gtk::PACK_EXPAND_WIDGET);
  m_MyWidget.show();

  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);

  m_ButtonBox.pack_start(m_Button_Quit, Gtk::PACK_SHRINK);
  m_ButtonBox.set_border_width(6);
  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
  m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this, &ExampleWindow::on_button_quit) );

  show_all_children();
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  hide();
}

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char *argv[])
{
  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  ExampleWindow window;

  //Shows the window and returns when it is closed.
  return app->run(window);
}

File: mywidget.cc (For use with gtkmm 3, not gtkmm 2)

#include "mywidget.h"
#include <gdkmm/general.h>  // for cairo helper functions
#include <iostream>
//#include <gtk/gtkwidget.h> //For GTK_IS_WIDGET()
#include <cstring>


MyWidget::MyWidget() :
  //The GType name will actually be gtkmm__CustomObject_mywidget
  Glib::ObjectBase("mywidget"),
  Gtk::Widget(),
  m_scale(1000)
{
  set_has_window(true);

  //This shows the GType name, which must be used in the CSS file.
  std::cout << "GType name: " << G_OBJECT_TYPE_NAME(gobj()) << std::endl;

  //This shows that the GType still derives from GtkWidget:
  //std::cout << "Gtype is a GtkWidget?:" << GTK_IS_WIDGET(gobj()) << std::endl;

  //Install a style so that an aspect of this widget may be themed via a CSS
  //style sheet file:
  gtk_widget_class_install_style_property(GTK_WIDGET_CLASS(
              G_OBJECT_GET_CLASS(gobj())),
      g_param_spec_int("example_scale",
        "Scale of Example Drawing",
        "The scale to use when drawing. This is just a silly example.",
        G_MININT,
        G_MAXINT,
        500,
        G_PARAM_READABLE) );

  m_refStyleProvider = Gtk::CssProvider::create();
  Glib::RefPtr<Gtk::StyleContext> refStyleContext = get_style_context();
  refStyleContext->add_provider(m_refStyleProvider, 
    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    
  try
  {
    m_refStyleProvider->load_from_path("custom_gtk.css");
  }
  catch(const Glib::Error& ex)
  {
    std::cerr << "Gtk::CssProvider::load_from_path() failed: " << ex.what() << std::endl;
  }
}

MyWidget::~MyWidget()
{
}

Gtk::SizeRequestMode MyWidget::get_request_mode_vfunc() const
{
  //Accept the default value supplied by the base class.
  return Gtk::Widget::get_request_mode_vfunc();
}

//Discover the total amount of minimum space and natural space needed by
//this widget.
//Let's make this simple example widget always need minimum 60 by 50 and
//natural 100 by 70.
void MyWidget::get_preferred_width_vfunc(int& minimum_width, int& natural_width) const
{
  minimum_width = 60;
  natural_width = 100;
}

void MyWidget::get_preferred_height_for_width_vfunc(int /* width */,
   int& minimum_height, int& natural_height) const
{
  minimum_height = 50;
  natural_height = 70;
}

void MyWidget::get_preferred_height_vfunc(int& minimum_height, int& natural_height) const
{
  minimum_height = 50;
  natural_height = 70;
}

void MyWidget::get_preferred_width_for_height_vfunc(int /* height */,
   int& minimum_width, int& natural_width) const
{
  minimum_width = 60;
  natural_width = 100;
}

void MyWidget::on_size_allocate(Gtk::Allocation& allocation)
{
  //Do something with the space that we have actually been given:
  //(We will not be given heights or widths less than we have requested, though
  //we might get more)

  //Use the offered allocation for this container:
  set_allocation(allocation);

  if(m_refGdkWindow)
  {
    m_refGdkWindow->move_resize( allocation.get_x(), allocation.get_y(),
            allocation.get_width(), allocation.get_height() );
  }
}

void MyWidget::on_map()
{
  //Call base class:
  Gtk::Widget::on_map();
}

void MyWidget::on_unmap()
{
  //Call base class:
  Gtk::Widget::on_unmap();
}

void MyWidget::on_realize()
{
  //Do not call base class Gtk::Widget::on_realize().
  //It's intended only for widgets that set_has_window(false).

  set_realized();

  //Get the themed style from the CSS file:
  get_style_property("example_scale", m_scale);
  std::cout << "m_scale (example_scale from the theme/css-file) is: "
      << m_scale << std::endl;

  if(!m_refGdkWindow)
  {
    //Create the GdkWindow:

    GdkWindowAttr attributes;
    memset(&attributes, 0, sizeof(attributes));

    Gtk::Allocation allocation = get_allocation();

    //Set initial position and size of the Gdk::Window:
    attributes.x = allocation.get_x();
    attributes.y = allocation.get_y();
    attributes.width = allocation.get_width();
    attributes.height = allocation.get_height();

    attributes.event_mask = get_events () | Gdk::EXPOSURE_MASK;
    attributes.window_type = GDK_WINDOW_CHILD;
    attributes.wclass = GDK_INPUT_OUTPUT;

    m_refGdkWindow = Gdk::Window::create(get_parent_window(), &attributes,
            GDK_WA_X | GDK_WA_Y);
    set_window(m_refGdkWindow);

    //set colors
    override_background_color(Gdk::RGBA("red"));
    override_color(Gdk::RGBA("blue"));

    //make the widget receive expose events
    m_refGdkWindow->set_user_data(gobj());
  }
}

void MyWidget::on_unrealize()
{
  m_refGdkWindow.reset();

  //Call base class:
  Gtk::Widget::on_unrealize();
}

bool MyWidget::on_draw(const Cairo::RefPtr<Cairo::Context>& cr)
{
  const double scale_x = (double)get_allocation().get_width() / m_scale;
  const double scale_y = (double)get_allocation().get_height() / m_scale;

  // paint the background
  Gdk::Cairo::set_source_rgba(cr, get_style_context()->get_background_color());
  cr->paint();

  // draw the foreground
  Gdk::Cairo::set_source_rgba(cr, get_style_context()->get_color());
  cr->move_to(155.*scale_x, 165.*scale_y);
  cr->line_to(155.*scale_x, 838.*scale_y);
  cr->line_to(265.*scale_x, 900.*scale_y);
  cr->line_to(849.*scale_x, 564.*scale_y);
  cr->line_to(849.*scale_x, 438.*scale_y);
  cr->line_to(265.*scale_x, 100.*scale_y);
  cr->line_to(155.*scale_x, 165.*scale_y);
  cr->move_to(265.*scale_x, 100.*scale_y);
  cr->line_to(265.*scale_x, 652.*scale_y);
  cr->line_to(526.*scale_x, 502.*scale_y);
  cr->move_to(369.*scale_x, 411.*scale_y);
  cr->line_to(633.*scale_x, 564.*scale_y);
  cr->move_to(369.*scale_x, 286.*scale_y);
  cr->line_to(369.*scale_x, 592.*scale_y);
  cr->move_to(369.*scale_x, 286.*scale_y);
  cr->line_to(849.*scale_x, 564.*scale_y);
  cr->move_to(633.*scale_x, 564.*scale_y);
  cr->line_to(155.*scale_x, 838.*scale_y);
  cr->stroke();

  return true;
}