Add draft for GNOME Shell extension creation post
This commit is contained in:
		
							
								
								
									
										132
									
								
								_drafts/writing-a-gnome-shell-extension.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								_drafts/writing-a-gnome-shell-extension.md
									
									
									
									
									
										Normal 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 GLib’s | ||||
| [Javascript example](https://github.com/gergelypolonkai/swe-glib/blob/master/examples/basic.js); | ||||
| it’s not beautiful, but it’s working), but wrapping your head around | ||||
| the Shell’s 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 panel’s right side when loaded. Upon unloading, | ||||
| the extension’s 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 parent’s `_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 | ||||
| let’s add some content. | ||||
		Reference in New Issue
	
	Block a user