Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Initialization

Unlike regular C++ classes, GObject classes do not have C++ constructors. Instead, they have GObject intializers.

Generally, GObject construction/initialization flow is somewhat complex, and explaining it is outside of the scope of this documentation. Please see “A gentle introduction to GObject construction” for one explanation of this process, and “Object construction” section of the GObject Tutorial for another one. This page documents the parts that are specific to using C++ and peel.

Instance initializer

When an instance of a GObject class is created, memory for the instance is allocated and zero-initialized. Then, the instance initializer is invoked. The instance inializer has the signature of void MyType::init (Class *) (you can ignore the class pointer most of the time). Note that unlike a C++ constructor, an instance initializer cannot accept any parameters (other than the class pointer); you should use properties for that use case instead.

Things you might want to do in an instance initializer of a GTK widget include:

  • Explicitly initializing any fields that should not be zero-initialized;
  • Setting up any child widgets;
  • Setting up event controllers.

A typical implementation of an instance initializer for a widget (in this case, a window) looks like this:

#include <peel/Gtk/Gtk.h>

using namespace peel;

class MyWindow final : public Gtk::Window
{
  PEEL_SIMPLE_CLASS (MyWindow, Gtk::Widget)

  inline void
  init (Class *);

  int last_index;
};

PEEL_CLASS_IMPL (MyWindow, "MyWindow", Gtk::Window)

inline void
MyWindow::init (Class *)
{
  /* fields that need initialization other than to 0 */
  last_index = -1;

  /* child widget */
  FloatPtr<Gtk::Button> button = Gtk::Button::create_with_label ("Click me");
  button->connect_clicked (this, &MyWindow::on_button_clicked);
  set_child (std::move (button));

  /* misc set-up */
  set_default_size (500, 300);
  action_set_enabled ("win.undo-close-tab", false);
}

Class initializer

The first time an instance of a class is created (or the class otherwise referenced), the initializer for the class itself is invoked. The class initializer has signature of void MyType::Class::init (), and is declared automatically by the PEEL_SIMPLE_CLASS macro.

In the class initializer, you set up the class structure (the MyType::Class) that is shared among all instances of the class and stores per-class data. Specifically:

A typical implementation of a class initializer for a widget looks like this:

#include <peel/Gtk/Gtk.h>

using namespace peel;

namespace Demo
{

class Gizmo final : public Gtk::Widget
{
  PEEL_SIMPLE_CLASS (Gizmo, Gtk::Widget)
  friend class Gtk::Widget::Class;

  inline void
  vfunc_dispose ();

  inline void
  vfunc_snapshot (Gtk::Snapshot *);
};

PEEL_CLASS_IMPL (Gizmo, "DemoGizmo", Gtk::Widget)

inline void
Gizmo::Class::init ()
{
  /* vfunc overrides */
  override_vfunc_dispose<Gizmo> ();
  override_vfunc_snapshot<Gizmo> ();

  /* widget class setup */
  set_css_name ("gizmo");
  set_layout_manager_type (Type::of<Gtk::BinLayout> ());
  install_action ("gizmo.frob", nullptr, /* ... */);
}

} /* namespace Demo */

Note that the C++ this pointer inside the class initializer refers to an instance of the class structure, which inherits from the base type’s class structure (here, Gizmo::Class inherits from Gtk::Widget::Class). This is what enables usage of the concise syntax for invoking class methods, such as Gtk::Widget::Class::set_css_name.

Constructors

Constructors (or constructor methods) in the GObject world are simply thin convenience wrappers over creating an instance of a type using Object::create (for Object-derived classes), while passing the values of the most relevant properties. Conventionally, constructors are named create, create_somehow, or create_with_something.

For example, a constructor for a label widget might look like this:

#include <peel/Gtk/Gtk.h>

using namespace peel;

namespace Demo
{

class Label final : public Gtk::Widget
{
  PEEL_SIMPLE_CLASS (Label, Gtk::Widget)

  /* ... */

public:
  PEEL_PROPERTY (String, text, "text")

  static FloatPtr<Label>
  create (const char *text);
};

FloatPtr<Label>
Label::create (const char *text)
{
  return Object::create<Label> (prop_text (), text);
}

} /* namespace Demo */

Most importantly, please note that such a constructor is not a single, canonical way to create an instance of the type. Object::create is such a way, and the constructor methods are merely convenient wrappers over calling Object::create. Indeed, in many cases, such as when constructing an instance using Gtk::Builder, the constructor will not be called. For this reason, you should avoid putting any logic into constructors.