classes, interfaces and properties in Javascript/gjs

Thought I’d share some more Javascript snippets with you zany kids.

Firstly, how to implement an interface (specifically a GInterface), for example an Mx.ItemFactory:

const ButtonFactory = new Lang.Class({
  Name: 'ButtonFactory',
  Extends: GObject.Object,
  Implements: [ Mx.ItemFactory ],
 
  _init : function (callback)
  {
    this.parent();
    this._callback = callback;
  },
 
  vfunc_create : function ()
  {
    let button = new Button();
    button.connect('clicked', Lang.bind(this, function ()
      {
        this._callback(button);
      }));
 
    return button;
  },
});

The important thing to notice is the vfunc_ that gets prepended to any calls you’re overriding from the library. Also be aware of your terminating commas, semicolons and braces. ((Obviously the entire class system here is a hack, which is what makes it so reminiscent of Perl.))

Of course, we want to bind extra attributes into the classes built by our factory (I’m not sure why Mx.ItemFactorys don’t just pass a row so that we can do whatever we like in the factory). Anyway, classes are easy in Javascript so we can just extend Mx.Button to include a property we can bind:

const Button = new Lang.Class({
  Name: 'Button',
  Extends: Mx.Button,
  Properties: {
    'id': GObject.ParamSpec.string('id', '', '',
        GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE,
        ''),
  },
 
  _init : function (params)
  {
    this.parent(params);
    this._id = '';
  },
 
  get id ()
  {
    return this._id;
  },
 
  set id (val)
  {
    this._id = val;
  },
});

Again be aware of missing commas.

For more examples take a look at testGObjectClass.js. You should also know that much of this requires a recent gjs, so it’ll be available in GNOME 3.4, but not 3.2.

If you’re interested, here’s the code that uses this factory:

let factory = new ButtonFactory(Lang.bind(this, function (button)
  {
    ...
  }));
 
let view = new Mx.ListView({
    'model': model,
    'factory': factory,
  });
 
view.add_attribute('label', 0);
view.add_attribute('id', 1);

Thanks to Jasper St. Pierre for the help.

Author: Danielle

Danielle is an Australian software engineer, computer scientist and feminist. She doesn't really work on GNOME any more (sadly). Opinions and writing are solely her own and so not represent her employer, the GNOME Foundation, or anyone else but herself.

6 thoughts on “classes, interfaces and properties in Javascript/gjs”

  1. I don’t think it’s reminiscent of Perl, I think it’s much more reminiscent of the adage “real programmers can write Fortran in any language”, but this time because of a poor API rather than programmer’s choice. The example code cries out for statically typed languages. Notice how 99% of that code is all just a big re-implementation of $conventions (static typing à la Java/C#) in terms of $other-conventions (property lists)? In JavaScript a much better API would be something like this:

    function my_handler() {
      // handle button clicked events here
    }
     
    var factory = new MxItemFactory({
      create: function() {
        var button =new Button();
        button.connect('clicked', my_handler);
        return button;
      }
    });
  2. Johan, you don’t need to create GObject Properties in order to make a complex application based on gjs. The shell never uses it, sushi never uses it, gnome-documents never uses it.

    The properties syntax is only there for when you *need* a GObject property, like if you’re interacting with Clutter’s animation framework (the primary motivation I had for writing the gjs property support). The syntax is awkward and clunky, I completely agree, but we don’t really have anything better, and I would suggest against using it unless absolutely necessary.

  3. I have to agree with Johan that it looks really bad – this is actually my experience with GObject bindings to most languages, even Vala suffers from this and it was being designed with GObject in mind. That, along with missing documentation for languages other than C make for a very messy experience.

  4. @Johan: so I think this particular API is naff. It’s not even easy to use in C. The point of this blog post was to show you how to do these things with gjs.

    The entire factory could be replaced by a callback that provides a row iter and returns a ClutterActor, which would make sense and be less work in both C and JS. I honestly struggle to think of cases where a full blown factory was useful.

    I prototyped a replacement api for Mx which looks a lot like the API you suggest, which works fine (unsurprisingly), but I haven’t yet proposed it for merging. Further by doing things like including the row in the callback, we can also avoid extending MxButton, and just set the information we need when creating the object.

  5. Handy!

    Next question: why am I getting a whole lotta TypeError: Lang.Class is not a constructor from my Shell extension? I hate computers.

Leave a Reply

Your email address will not be published. Required fields are marked *

Creative Commons Attribution-ShareAlike 2.5 Australia
This work by Danielle Madeley is licensed under a Creative Commons Attribution-ShareAlike 2.5 Australia.