Skip to content

phaylon/MooseX-Gtk2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NAME

MooseX::Gtk2 - Connect Moose with Gtk2

VERSION

version 0.01

SYNOPSIS

package MyWindow;
use MooseX::Gtk2;

extends 'Gtk2::Window';

has button => (
    is          => 'ro',
    isa         => 'Gtk2::Button',
    builder     => '_build_button',
);

sub _build_button { Gtk2::Button->new('Hello World') }

sub BUILD {
    my ($self) = @_;
    $self->add($self->button);
}

register;

And later:

use MyWindow;

my $window = MyWindow->new(title => 'My App');
$window->show_all;

Gtk2->main;

DESCRIPTION

This extension allows you to use Moose to declare Glib classes that are fit to be used as objects in Gtk2 applications. You cannot simply subclass a Gtk2::Widget and put it into a Gtk2::VBox. The class has to be registered with Glib as an object class first. This is what the call to "register" accomplishes in the "SYNOPSIS".

Note that you don't have to load Gtk2 with the -init switch or call Gtk2->init yourself. This extension will make sure the package MooseX::Gtk2::Init is loaded which ensures initialization has taken place.

While the behaviour of classes declared with this module is a bit different than the use of their Gtk2 or Glib parents, the original classes will not be changed. Most of the differences are for maintainability's sake. I tried to conform to Glib/Gtk2 practices where appropriate.

Class Definitions

The class definitions look very similar to normal Moose classes. First you need to use MooseX::Gtk2 to initialize your metaclasses and import the necessary declarative callbacks:

package MyWidget;
use MooseX::Gtk2;

Note that you don't have to use Moose, since MooseX::Gtk2 uses Moose::Exporter and automatically sets up Moose as well.

You will then usually define a class to extend with the "extends" keyword:

extends 'Gtk2::Button';

If you don't specify a parent class, your new class will be based on Glib::Object. This can be useful if you want to build non-widget classes which support Glib signals.

Since Glib only allows single inheritance, you will not be able to inherit from more than one class at a time. Even if Glib would support this, it would probably be limited to avoid edge cases.

You can declare "Properties" with "has" and apply roles via "with" as usual. If you want your roles to provide signals, you'll have to use MooseX::Gtk2::Role as outlined in "Role Definitions".

Unlike normal Perl classes, the Glib has to be told about your class before it wants anything to do with it. This is why classes declared with this module have to finalize themselves with a call to "register":

register;

This function will throw an error when something goes wrong and return a true value otherwise, so you can at least skip the 1; in these classes. Calling this will effectively lock the class down, so make sure you do any declarations and role applications before this.

After this, your class is ready to use.

Role Definitions

MooseX::Gtk2::Roles are mostly just like Moose::Roles:

package MyRole;
use MooseX::Gtk2::Role;

requires qw( _handle_an_event );

has a_property => (is => 'rw');

signal an_event => (handler => '_handle_an_event');

with qw( MyOtherRole );

1;

Notice the 1; at the end? Since Glib only cares about the structure of the final classes, you don't have to register the roles you apply to your classes during declaration.

An error will be thrown if you try to compose a MooseX::Gtk2::Role into something that doesn't know how to handle signals.

Object Construction

When you call new on one of your declared classes, you always have to pass in key/value pairs, no matter what your parent expects. All constructors are overridden, so we don't have to maintain lots of special cases. This means that while you can create a new Gtk2::Window like this:

Gtk2::Window->new('toplevel');

When you want to do the same with your own window class, you'd have to do:

YourWindowClass->new(type => 'toplevel');

However, you can use BUILDARGS (explained in "BUILDARGS" in Moose::Object) to modify the arguments the constructor accepts, just like regular Moose classes.

You can also use BUILD and DEMOLISH as usual.

Properties

What Moose calls an attribute is known to Glib as a property. If you create an attribute on your subclass like the following:

has some_attribute => (
    is          => 'rw',
    isa         => 'Str',
    required    => 1,
);

Your attribute will automatically be prepared to be registered with Glib when you call "register".

Usually the is option controls if you receive an accessor or just a reader. In this case, however, it will have two different implications:

  • Generated Methods

    You'll get a reader named get_some_attribute and set_some_attribute instead of a reader or an accessor with the name of the attribute. This is just the default, of course. It was chosen to maintain symmetry with Glib conventions.

  • General Access

    A Glib::Object always also has a set and a get method that work like the following:

    $object->set(foo => 23, bar => 17);
    my ($foo, $bar) = $object->get(qw( foo bar ));

    Unless your parent class overrides these methods (Gtk2::TreeStore is an example that uses these methods to set row column values), they will be overridden so your type constraints are respected (Note: This is only true in Perl-space). The same is true for the get_property/set_property aliases of get/set.

It is also possible to extend Glib properties inside your class:

package MyWindow;
use MooseX::Gtk2;

extends 'Gtk2::Window';

has '+title' => (isa => 'MyTypeConstraint');

register;

Be careful, however, since full compatibility with all systems that access these values can not be guaranteed at all.

In all cases, things like trigger, isa and lazy defaults will probably not work correctly when triggered inside Glib. This might change over time, since it should be possible to emulate these behaviours via the GET_PROPERTY/SET_PROPERTY facilities.

Signals

Signals are specific to Glib and have no Moose equivalent. You can declare a new signal with the "signal" keyword:

signal new_signal => (@options);

See "signal" for all available options. Signals and overrides can be declared in classes as well as in roles. They can only be declared once per class, and you cannot override a signal in the same class as you declare it on. These restrictions include signals that are composed in via roles.

EXPORTS

All of these are exported by default.

extends

extends 'ParentClass';

Declares the parent class, which needs to be a subclass of Glib::Object. Multiple inheritance is not supported.

In case your parent class is a Glib::Object that is not yet a MooseX::Gtk2 extended class, your subclass will actually not directly depend on the parent class, but on a membrane class which maps the Glib identity of the class into Moose terms. This is the layer that makes things such as attributes for existing Gtk2 properties possible.

has

has attribute_name => (%options);

The possible options are the same as for normal Moose attributes. You can also extend existing attributes (even those provided by Glib objects) as normal with Moose:

has '+existing_attribute' => (%new_options);

Not all functionality of attributes might be avilable when trigger from inside Glib.

signal

signal signal_name => (%options);

The above will simply declare a new signal. You can additionally override an existing signal handler via:

signal existing_signal => \&signal_handler_override;

The subroutine provided instead of a set of options will be used as new signal handler. Overrides can only be done for signals provided above the current class they are added to (including from roles). Signals and overrides are also only settable once.

A signal defined like this can be called like the usual Glib signals:

my $return = $object->signal_emit(signal_name => $object, @args);

The available signal options are:

arity

Declares the number of arguments. Defaults to 0. The widget on which the signal is emitted is always expected to be the first argument and is not counted in the arity setting. Here is an example of a different arity:

signal add => (
    arity   => 2,
    handler => '_handle_add',
);

sub _handle_add {
    my ($self, $num1, $num2) = @_;
    return $num1 + $num2;
}

$object->signal_emit(add => $object, 2, 3); # returns 5
runs

Can be one of first, last or cleanup. Defaults to last. This run type regulates when the handler will be run in the signal chain, and what callback is able to return a value.

handler

This should be either a method name or a code reference. The handler will be called when the signal was emitted. When it will run depends on the value passed to "runs".

restart

If set to true, a signal will be restarted instead of running recursively when it is fired while it is being handled.

collect

The accumulator function. You probably don't want this unless you know that you do.

register

register;

Requires no arguments. This must be called last in your class to register it with Glib. It will return a true value so you don't have to do that.

After this function is called, your class will also be immutable, and it will stay that way. Once Glib knows about your class, nothing can be changed.

with

It's the same as usual.

CAVEATS

Object Reference Type

Only blessed hash references can be used. Trying to make it compatible with something else would be a mess.

Single Inheritance

Glib only supports single inheritance. So we do the same.

BUILDALL and DEMOLISHALL

Don't hook into these, since they might not work as expected. Destruction is handled completely by Glib, since your object's lifetime might be longer than that of the reference you have of it.

If you provide a DEMOLISH method, calls to it will be emulated by hooking into Glib's FINALIZE_INSTANCE. The methods BUILD and BUILDARGS should be available as usual.

Possible Attribute Value Bypasses

Things that are set by Glib code outside of Perl or even just outside MooseX::Gtk2, might bypass specific functions of Moose and set the values directly. This needs to be emulated carefully, but there might always be ways around the MOP.

Replacement of Getters and Setters

For attributes that are loaded from non-MooseX::Gtk2 classes, if we can find set_* and get_* methods that have the right name, we'll assume they are related to the attribute.

Interfaces

Currently not supported at all.

Dynamic Class Management

Since we have to register every class with Glib or it won't be usable as such, things like anonymous classes, or role-to-object application will not work.

TODO

  • Provide requires_signals(@signal_names) for roles.

  • Support "INTERFACES" in Glib::Object::Subclass.

  • Allow to provide Glib types other than Glib::Scalar.

  • Option to turn of implicit invocant signal parameter.

  • More tests.

SEE ALSO

BUGS

Please report any bugs or feature requests to bug-moosex-gtk2@rt.cpan.org or through the web interface at: http://rt.cpan.org/Public/Dist/Display.html?Name=MooseX-Gtk2

AUTHOR

Robert 'phaylon' Sedlacek <rs@474.at>

COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Robert 'phaylon' Sedlacek.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

About

Connect Moose with Gtk2

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages