Add draft for GNOME Shell extension creation post

This commit is contained in:
Gergely Polonkai 2015-09-28 17:12:52 +02:00
parent 9cfd8a19a7
commit 2b14481253

View File

@ -0,0 +1,132 @@
---
layout: post
title: "Writing a GNOME Shell extension"
---
I could not find a tutorial on how to write a GNOME Shell extension,
but I wanted to create one for my SWE GLib library to show the current
positions of the planets. So I dug into existing (and working)
extensions source code and made up something. Comments welcome!
---
GNOME Shell extensions are written in JavaScript and are interpreted
by [GJS](https://wiki.gnome.org/action/show/Projects/Gjs). Using
introspected libraries from JavaScript is not a problem for me (see
SWE GLibs
[Javascript example](https://github.com/gergelypolonkai/swe-glib/blob/master/examples/basic.js);
its not beautiful, but its working), but wrapping your head around
the Shells concept can take some time.
The Shell is a Clutter stage, and all the buttons (including the
top-right “Activities” button) are actors on this stage. You can add
practically anything to the Shell panel that you can add to a Clutter
stage.
The other thing to remember is the lifecycle of a Shell extension. There
are two methods here: either you use an extension controller, or plain
old Javascript functions `enable()` and `disable()`; I will go on with
the former method.
## Anatomy of an extension
The only thing you actually need is an `init()` function:
function init(extensionMeta) {
// Do whatever it takes to initialize your extension,
// like initializing the translations.
return new ExtensionController(extensionMeta);
}
## Extension controller
So far so good, but what is this extension controller thing? It is an
object which is capable of managing your GNOME Shell extension. Whenever
the extension is loaded, its `enable()` method is called; when the
extension is unloaded, the `disable()` method gets called.
function ExtensionController(extensionMeta) {
return {
extensionMeta: extensionMeta,
extension: null,
enable: function() {
this.extension = new PlanetsExtension(this.extensionMeta);
Main.panel.addToStatusArea("planets", this.extension, 0, "right");
},
disable: function() {
this.extension.actor.destroy();
this.extension.destroy();
this.extension = null;
}
}
}
This controller will create a new instance of the `PlanetsExtension`
class and add it to the panels right side when loaded. Upon unloading,
the extensions actor gets destroyed, together with the extension
itself. Also, for safety measures, the extension is set to `null`.
As you will see soon, `extension.actor` is not created by us, but behind
the scenes by the Shell.
## The extension
The extension is a bit more tricky, as, for convenience reasons, it
should extend an existing panel widget type.
```
function PlanetsExtension(extensionMeta) {
this._init(extensionMeta);
}
PlanetsExtension.prototype = {
__proto__ = PanelMenu.Button.prototype,
_init: function(extensionMeta) {
PanelMenu.Button.prototype._init.call(this, 0.0);
this.extensionMeta = extensionMeta;
this.panelContainer = new St.BoxLayout({style_class: 'panel-box'});
this.actor.add_actor(this.panelContainer);
this.actor.add_style_class_name('panel-status-button');
this.panelLabel = new St.Label({text: 'Loading', y_align: Clutter.ActorAlign.CENTER});
this.panelContainer.add(this.panelLabel);
}
};
```
The only parameter passed to the parents `_init` function is
`menuAlignment`, with the value `0.0`, which is used to position the
menu arrow. (_Note: I cannot find any documentation on this, but it
seems that with the value `0.0`, a menu arrow is not added._)
## Loading up the extension
Now with the correct import lines added:
const PanelMenu = imports.ui.panelMenu;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;
The only thing to create now is the `metadata.json` file. It contains
compatibility information and, well, some meta data.
{
"shell-version": ["3.18"],
"uuid": "planets@gergely.polonkai.eu",
"name": "Planets",
"description": "Display current planet positions"
}
As soon as this file is ready, you can restart your Shell (Alt-F2, enter
the command `r`), and load the extension with e.g. the GNOME Tweak Tool.
This little label showing the static text “Planets” is pretty boring, so
lets add some content.