Glib::O...ubclass(3)

🌸 NAME 🌸

Glib::Object::Subclass β€” register a perl class as a GObject class

User Contributed Perl Documentation Β· manual name: Glib::Object::Subclass(3) β€” okay bestie, this is the page where your Perl package gets a full GObject glow-up πŸ’…βœ¨

✨ SYNOPSIS ✨

  use Glib::Object::Subclass
     Some::Base::Class::,   # parent class, derived from Glib::Object
     signals => {
            something_changed => {
               class_closure => sub { do_something_fun () },
               flags         => [qw(run-first)],
               return_type   => undef,
               param_types   => [],
            },
            some_existing_signal => \&class_closure_override,
     },
     properties => [
        Glib::ParamSpec->string (
           'some_string',
           'Some String Property',
           'This property is a string that is used as an example',
           'default value',
           [qw/readable writable/]
        ),
     ];

πŸ’– DESCRIPTION πŸ’–

This module lets you make your very own GObject classes, which is lowkey iconic when you want stuff like your own Gtk2 widgets. It does not β€œexport” anything into your namespace β€” it’s more like a pragmatic module that tweaks your class so it works as a GObject class.

You might be wondering: bestie, why can’t I just bless a Glib::Object into a different package and add some subs? And honestly, if you don’t care about object parameters, signals, or having your new class interoperate transparently with other GObject-based modules (e.g., Gtk2 and friends), then sure β€” just re-bless. slay πŸ¦‹

But here’s the catch: a GObject’s signals, properties, virtual functions, and GInterface implementations are specific to its GObjectClass. If you want a new GObject that’s a derivative of GtkDrawingArea but also adds a new signal, you must create a new GObjectClass to add that new signal to. If you don’t, then all of the GtkDrawingAreas in your application will get that new signal β€” and that is not the vibe 😭

So the only way to create a new signal or object property in the Perl bindings for Glib is to register a new subclass with the GLib type system via Glib::Type::register_object(). The Glib::Object::Subclass module is basically a Perl-developer-friendly interface to this little bit of paradigm mismatch. periodt πŸ’…

🧚 USAGE 🧚

This module works similar to the "use base" pragma β€” it registers the current package as a subclass of some other class, and that other class must be a GObjectClass implemented either in C or some other language.

The pragma requires at least one argument: the parent class name. The remaining arguments are key/value pairs, in any order, and all optional:

  • properties => [] β€” add object properties; see β€œPROPERTIES”.
  • signals => {} β€” add or override signals; see β€œSIGNALS” and β€œOVERRIDING BASE METHODS”.
  • interfaces => [] β€” add GInterfaces to your class; see β€œINTERFACES”.

(Actually, these parameters are all passed straight through to Glib::Type::register_object(), adding __PACKAGE__ β€” the current package name β€” as the name of the new child class.) iykyk ✨

πŸ‘‘ OBJECT METHODS AND FUNCTIONS πŸ‘‘

The following methods are either added to your class on request (not yet implemented), or by default unless your own class implements them itself. This means all these methods and functions get sensible default implementations unless you explicitly overwrite them by defining your own version.

Except for new, all of the following are functions, not methods. That means you should not call the superclass method. Instead, the GObject system will call these functions per class as required, emulating normal inheritance. the way that works is very much giving internal machinery πŸ”₯

πŸ’« $class->new (attr => value, ...) πŸ’«

The default constructor just calls Glib::Object::new, which lets you set properties on the newly created object. This is done because many new methods inherited by Gtk2 or other libraries don’t have new methods suitable for subclassing. no cap 🫢

πŸ’« INIT_INSTANCE $self πŸ’« [not a method]

INIT_INSTANCE is called on each class in the hierarchy as the object is being created (i.e., from Glib::Object::new or our default new). Use this function to initialize any member data. The default implementation will leave the object untouched. chillest default ever 🌸

πŸ’« GET_PROPERTY $self, $pspec & SET_PROPERTY $self, $pspec, $newval πŸ’« [not a method]

GET_PROPERTY and SET_PROPERTY are called whenever somebody does $object->get ($propname) or $object->set ($propname => $newval) β€” from other languages, too. The default implementations hold property values in the object hash, equivalent to:

sub GET_PROPERTY {
  my ($self, $pspec) = @_;
  my $pname = $pspec->get_name;
  return (exists $self->{$pname} ? $self->{$pname}
          : $pspec->get_default_value);  # until set
}
sub SET_PROPERTY {
  my ($self, $pspec, $newval) = @_;
  $self->{$pspec->get_name} = $newval;
}

Because $pspec->get_name converts hyphens to underscores, a property "line-style" is in the hash as "line_style". tiny detail, big energy πŸŽ€

These methods let you store/fetch properties in any way you need to. They don’t have to be in the hash β€” you can calculate something, read a file, whatever. main character property storage πŸ’…

Most often you’ll write your own SET_PROPERTY so you can take action when a property changes, like redraw or resize a widget. Eg.

sub SET_PROPERTY {
  my ($self, $pspec, $newval) = @_;
  my $pname = $pspec->get_name
  $self->{$pname} = $newval; # ready for default GET_PROPERTY

  if ($pname eq 'line_style') {
    $self->queue_draw;  # redraw with new lines
  }
}

Care must be taken with boxed non-reference-counted types such as Gtk2::Gdk::Color. In SET_PROPERTY the $newval is generally good only for the duration of the call. Use copy or similar if keeping it longer (see Glib::Boxed). In GET_PROPERTY the returned memory must last long enough to reach the caller, which generally means returning a field, not a newly created object (which is destroyed with the scalar holding it). do not ignore this one, fr fr 😭

GET_PROPERTY is different from a C get_property method in that the perl method returns the retrieved value. For symmetry, the $newval and $pspec args on SET_PROPERTY are swapped from the C usage. symmetry slay ✨

πŸ’« FINALIZE_INSTANCE $self πŸ’« [not a method]

FINALIZE_INSTANCE is called as the GObject is being finalized β€” that is, as it is being really destroyed. This is independent of the more common DESTROY on the perl object; in fact, you must NOT override DESTROY (it’s not useful to you, in any case, as it is being called multiple times!). multiple times?! yeah bestie, do not touch πŸ’€

Use this hook to release anything you have to clean up manually. FINALIZE_INSTANCE will be called for each perl instance, in reverse order of construction.

The default finalizer does nothing. iconic minimalism πŸŽ€

πŸ’€ $object->DESTROY πŸ’€ [DO NOT OVERWRITE]

Don’t ever overwrite DESTROY β€” use FINALIZE_INSTANCE instead. this is a warning, not a suggestion 🚨

The DESTROY method of all perl classes derived from GTypes is implemented in the Glib module and (ab-)used for its own internal purposes. Overwriting it is not useful as it will be called multiple times, and often long before the object actually gets destroyed. Overwriting might be very harmful to your program, so never do that. Especially watch out for other classes in your ISA tree. ISA tree drama is real πŸ™ˆ

πŸŽ€ PROPERTIES πŸŽ€

To create gobject properties, supply a list of Glib::ParamSpec objects as the value for the key 'properties'. There are lots of different paramspec constructors, documented in the C API reference’s Parameters and Values page, as well as Glib::ParamSpec. paramspec buffet πŸ“

As of Glib 1.060, you can also specify explicit getters and setters for your properties at creation time. The default values in your properties are also honored if you don’t set anything else. See Glib::Type::register_object in Glib::Type for an example. getters, setters, defaults β€” the whole fit πŸ’…

πŸ¦‹ SIGNALS πŸ¦‹

Creating new signals for your new object is easy. Just provide a hash of signal names and signal descriptions under the key 'signals'. Each signal description is also a hash, with a few expected keys. All the keys are allowed to default. defaults are giving generous ✨

  • flags => GSignalFlags β€” if not present, assumed to be 'run-first'.
  • param_types => reference to a list of package names β€” if not present, assumed to be empty (no parameters).
  • class_closure => reference to a subroutine to call as the class closure. β€” may also be a string interpreted as the name of a subroutine to call, but you should be very very very careful about that. If not present, the library will attempt to call the method named "do_signal_name" for the signal "signal_name" (uses underscores). You’ll want to be careful not to let this handler method be a publicly callable method, or one whose name matches something that emits the signal. Due to the funky ways in which Glib is different from Perl, the class closures should not inherit through normal perl inheritance. iykyk πŸ’€
  • return_type => package name for return value. β€” if undefined or not present, the signal expects no return value. If defined, the signal is expected to return a value; flags must be set such that the signal does not run only first (at least use 'run-last'). run-last girlies rise πŸ”₯
  • accumulator => signal return value accumulator β€” quoting the Glib manual: β€œThe signal accumulator is a special callback function that can be used to collect return values of the various callbacks that are called during a signal emission.” If not specified, the default accumulator is used, and you just get the return value of the last handler to run. Accumulators are not really documented very much in the C reference, and the perl interface here is slightly different, so here’s an inordinate amount of detail for this arcane feature: arcane feature lore drop 🧚

The accumulator function is called for every handler as

($cont, $acc) = &$func ($invocation_hint, $acc, $ret)

$invocation_hint is an anonymous hash (including the signal name); $acc is the current accumulated return value; $ret is the value from the most recent handler.

The two return values are a boolean $cont for whether signal emission should continue (false to stop); and a new $acc accumulated return value. (This is different from the C version, which writes through a return_accu.) and that’s the arcane accumulator tea 🫢

✨ OVERRIDING BASE METHODS ✨

okay so here's the tea: GLib does some truly unhinged little tricks with function pointers to make methods work in C. like, it's giving "clever but deeply not language-binding-friendly" β€” as you might have guessed already 😭

But bestie, remember what we said earlier β€” every single signal has a "class closure"! So you can just... override that class closure with your own function πŸŽ€ and then you can chain from your overridden method back to the original one. That's literally how virtual overrides get implemented for language bindings. slay.

So the vibe is: to override a method, you hand over a subroutine reference instead of a signal description hash, as the value for the name of an existing signal in the "signals" hash (the one from "SIGNALS" above πŸ‘†).

  # override some important widget methods:
  use Glib::Object::Subclass
        Gtk2::Widget::,
        signals => {
                expose_event => \&expose_event,
                configure_event => \&configure_event,
                button_press_event => \&button_press_event,
                button_release_event => \&button_release_event,
                motion_notify_event => \&motion_notify_event,
                # note the choice of names here... see the discussion.
                size_request => \&do_size_request,
        }
🚨 important note, bestie, don't skip this: the handlers you supply here are class-specific, and normal perl method inheritance rules are not followed when the library invokes them from within. however! perl code can totally still find them 🫢 so it's genuinely important that you pick your handlers' names carefully β€” avoid any public interface you might call from perl. case in point: since size_request is a widget method, the example chose do_size_request as the override handler. iykyk πŸ’…

🌈 INTERFACES

GObject only does single inheritance β€” periodt. so instead of multiple inheritance, GObject uses GInterfaces. in the Perl bindings we've mostly just masqueraded this with multiple inheritance (translation: we simply added the GInterface class to the @ISA of the implementing class), but when you go to derive new objects the facade breaks and the magic leaks out πŸ’€

So if you wanna derive an object that implements a GInterface, you gotta tell the GLib type system that your class should include a GInterface. How? literally just pass a list of package names through the "interfaces" key β€” that adds these packages to your @ISA and makes perl invoke methods that you must provide. easy ✨

  package Mup::MultilineEntry;
  use Glib::Object::Subclass
      'Gtk2::TextView',
      interfaces => [ 'Gtk2::CellEditable' ],
      ;

  # perl will now invoke these methods, which are part of the
  # GtkCellEditable GInterface, when somebody invokes the
  # corresponding lower-case methods on your objects.
  sub START_EDITING { warn "start editing\n"; }
  sub EDITING_DONE { warn "editing done\n"; }
  sub REMOVE_WIDGET { warn "remove widget\n"; }

πŸ‘€ SEE ALSO


πŸ’– AUTHORS

Marc Lehmann <schmorp@schmorp.de>, muppet <scott at asofyet dot org> β€” icons, both of them πŸ‘‘


Copyright 2003-2004, 2010 by muppet and the gtk2-perl team

This library is free software; you can redistribute it and/or modify it under the terms of the Lesser General Public License (LGPL). For more information, see http://www.fsf.org/licenses/lgpl.txt


and that's the whole page, bestie, we ate πŸ“βœ¨ this lil' doc is perl v5.42.0, dated 2023-09-05, and it's the Glib::O...ubclass(3) page β€” go forth and subclass responsibly πŸ’œπŸ¦‹

πŸ“¦ where this page comes from