Translate

Archives

Updating GNOME Shell Extensions To Work With GNOME 3.2

This particular post will contain the knowledge that I accumulate of the next few weeks about updating GNOME 3.0 Shell extensions so that they work with the GNOME 3.2 Shell. It will be a living document for a period of about 2 months so check back frequently for updates. When the GNOME 3.4 Shell is released, it will be covered in a separate post.

As you are probably aware, GNOME Shell comes with an interactive GNOME Shell extension creator called gnome-shell-extension-tool. The utility is actually a simple Python script. It generates a simple Hello World extension using:

gnome-shell-extension-tool --create-extension


After asking you 3 questions, the utility generates three files which are placed in $HOME/.local/share/gnome-shell/extensions/<EXTENSION_UUID>/.

-rw-rw-r--. 1 fpm fpm 1488 Oct 25 21:46 extension.js
-rw-rw-r--. 1 fpm fpm  152 Oct 26 12:30 metadata.json
-rw-rw-r--. 1 fpm fpm  172 Oct 25 21:46 stylesheet.css

Here is what is generated by this utility for extension.js on Fedora 15 which uses GNOME 3.0:

const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Main = imports.ui.main;
 
function _showHello() {
    let text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
    let monitor = global.get_primary_monitor();
    global.stage.add_actor(text);
    text.set_position(Math.floor (monitor.width / 2 - text.width / 2),
                      Math.floor(monitor.height / 2 - text.height / 2));
    Mainloop.timeout_add(3000, function () { text.destroy(); });
}
 
// Put your extension initialization code here
function main() {
    Main.panel.actor.reactive = true;
    Main.panel.actor.connect('button-release-event', _showHello);
}


Thanks to the seemingly tireless Jasper St. Pierre, here is a slightly more advanced version of a GNOME 3.0 HelloWorld Shell extension which was backported from the code generated by the same utility on Fedora 16 beta.

const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;

let text, button;

function _hideHello() {
    Main.uiGroup.remove_actor(text);
    text = null;
}

function _showHello() {
    if (!text) {
        text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
        Main.uiGroup.add_actor(text);
    }

    text.opacity = 255;

    let monitor = Main.layout.primaryMonitor;

    text.set_position(Math.floor(monitor.width / 2 - text.width / 2),
                      Math.floor(monitor.height / 2 - text.height / 2));

    Tweener.addTween(text,
                     { opacity: 0,
                       time: 2,
                       transition: 'easeOutQuad',
                       onComplete: _hideHello });
}

function main() {
    button = new St.Bin({ style_class: 'panel-button',
                          reactive: true,
                          can_focus: true,
                          x_fill: true,
                          y_fill: false,
                          track_hover: true });
    let icon = new St.Icon({ icon_name: 'system-run',
                             icon_type: St.IconType.SYMBOLIC,
                             style_class: 'system-status-icon' });

    button.set_child(icon);
    button.connect('button-press-event', _showHello);
    Main.panel._rightBox.insert_actor(button, 0);
}


This extension simply adds a button to the right hand side of the (top) panel and displays a Hello World message for a number of seconds. I am going to assume that you are familiar with JavaScript and GNOME Shell extensions in general if you are reading this post – so I am not going to explain the code.

GNOME 3.2 extension image

In GNOME 3.0, all Shell extensions are enabled by default unless the Shell extension is in a blacklist, i.e. org.gnome.shell.disabled-extensions. Per-user and systemwide Shell extensions can be disabled with this GSettings key. Thus, this Shell extension is enabled by default and loaded when you restart the Shell using Alt+F2 R, gnome-shell –replace or some other means.

To gain an understanding of what changes may be required in a 3.0 Shell extension in order for the extension to work in GNOME 3.2, we examine the code generated by this utility for a 3.2 Shell extension. By the way, with Fedora 16 Beta you may get the following error when you run this utility:

Uuid [example__@ultra.xfpmurphy.com]: helloworld@example.com                      
Created extension in '/home/fpm/.local/share/gnome-shell/extensions/helloworld@example.com'
Traceback (most recent call last):
  File "/usr/bin/gnome-shell-extension-tool", line 151, in 
    subprocess.Popen(['gnome-open', extensionjs_path])
NameError: name 'extensionjs_path' is not defined


See this bug report for more information. The fix is simple – add the line denoted by the +.

     print "Created extension in %r" % (extension_path, )
+    extensionjs_path = os.path.join(extension_path, 'extension.js')
     subprocess.Popen(['xdg-open', extensionjs_path])
     sys.exit(0)


You may notice that I also replaced gnome-open with xdg-open in subprocess.Popen. That is because gnome-open is deprecated.

Here is what is generated by the gnome-shell-extension-tool on Fedora 16 Beta:

const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;

let text, button;

function _hideHello() {
    Main.uiGroup.remove_actor(text);
    text = null;
}

function _showHello() {
    if (!text) {
        text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
        Main.uiGroup.add_actor(text);
    }

    text.opacity = 255;

    let monitor = Main.layoutManager.primaryMonitor;

    text.set_position(Math.floor(monitor.width / 2 - text.width / 2),
                      Math.floor(monitor.height / 2 - text.height / 2));

    Tweener.addTween(text,
                     { opacity: 0,
                       time: 2,
                       transition: 'easeOutQuad',
                       onComplete: _hideHello });
}

function init() {
    button = new St.Bin({ style_class: 'panel-button',
                          reactive: true,
                          can_focus: true,
                          x_fill: true,
                          y_fill: false,
                          track_hover: true });
    let icon = new St.Icon({ icon_name: 'system-run',
                             icon_type: St.IconType.SYMBOLIC,
                             style_class: 'system-status-icon' });

    button.set_child(icon);
    button.connect('button-press-event', _showHello);
}

function enable() {
    Main.panel._rightBox.insert_actor(button, 0);
}

function disable() {
    Main.panel._rightBox.remove_actor(button);
}


As you can see it is quite different than the backported GNOME 3.0 HelloWorld Shell extension. The basic functionality of the Shell extension is the same. There is no longer a main function; it has been replaced by init. Also two functions or methods named enable and disable are mandatory. More about these later.

In GNOME 3.2, a Shell extension is not available to you by default when the Shell is restarted because, in GNOME 3.2, Shell extensions are no longer enabled by default. The Shell extension is loaded but not enabled. Looking Glass will show the Shell extension in the Extensions pane but it will display as disabled. Any errors in the Shell extension code will be displayed in the Errors pane.

GNOME 3.2 extension image

To become enabled, a Shell extension must be specifically added to a whitelist, i.e. the GSettings org.gnome.shell.enabled-extensions key. Then the enable method or function in the Shell extension must be called. Typically this automatically happens – more about this later.

Suppose a Shell extension is named example32@example.com. One way to enable the Shell extension is to use the gsettings utility:

% gsettings get org.gnome.shell enabled-extensions[]
% gsettings set org.gnome.shell enabled-extensions "['example32@example.com']"
% gsettings get org.gnome.shell enabled-extensions
['example@example.com']


Another way to enable or disable the Shell extension is to use John Stower‘s excellent gnome-tweet-tool or the dconf-editor utility.

dconf-editor

Another way is to use a future version of gnome-shell-extension-tool, not yet released as of the date of this blog, which has options to enable or disable extensions.

$ ./gnome-shell-extension-tool --help
Usage: gnome-shell-extension-tool [options]

Options:
  -h, --help            show this help message and exit
  -d DISABLE, --disable-extension=DISABLE
                        Disable a GNOME Shell extension
  -e ENABLE, --enable-extension=ENABLE
                        Enable a GNOME Shell extension
  -c, --create-extension
                        Create a new GNOME Shell extension

$ gnome-shell-extension-tool -e example32@example.com
'example32@example.com' is now enabled.
$ gnome-shell-extension-tool -d example32@example.com
'example32@example.com' is now disabled.


A copy of this new version of gnome-shell-extension-tool is available here. Note that it does not check the validity of extension names!

By the way, while it is not specifically stated in the GNOME 3.2 Release Notes, from looking at the GNOME 3.2 Shell source code, it would appear that the org.gnome.shell.disabled-extensions key is no longer used to blacklist Shell extensions.

If you compare the 3.0 and 3.2 versions of the Shell extension code, the main function in 3.0 is replaced by three functions in 3.2, i.e. init, enable and disable. The init function is mandatory just like main was in 3.0. It does everything the main function usually does, except it cannot change any Shell state. Its purpose is to setup whatever needs to be setup, such as labels, text, icons or actors, prior to enabling the extension. It is only called once per shell session. The enable and disable functions are mandatory and are intended to be used to make a Shell extension’s UI visible (enable) or invisible (disable) to the user.

Note that while enable and disable can be empty functions and init can contain the code to enable the UI, it goes against what the GNOME Shell developers are trying to achieve, i.e. provide a way to enable or disable a Shell extension without having to reload the GNOME Shell. Remember, in GNOME Shell 3.0, every time you wished to do something with an extension you had to reload the GNOME Shell.

This design is not enforced; it cannot be. All the Shell loadExtension function, which was extensively modified in 3.2 (see extensionSystem.js), can do is to check for the existence of init, enable and disable. This is all the Shell can do to support the intent of the new design without extensive code being added to the Shell. It is up to writers of Shell extensions to comply with the spirit and intent of the design. When the proposed extensions.gnome.org materializes, a tool could probably be written to check that enable and disable actually enable or disable the Shell extension UI as part of the acceptable criteria for repository.

    if (!extensionModule.init) {
        logExtensionError(uuid, 'missing \'init\' function');
        return;
    }

    try {
        extensionState = extensionModule.init(meta);
    } catch (e) {
        if (stylesheetPath != null)
            theme.unload_stylesheet(stylesheetPath);
        logExtensionError(uuid, 'Failed to evaluate init function:' + e);
        return;
    }

    if (!extensionState)
        extensionState = extensionModule;
    extensionStateObjs[uuid] = extensionState;

    if (!extensionState.enable) {
        logExtensionError(uuid, 'missing \'enable\' function');
        return;
    }
    if (!extensionState.disable) {
        logExtensionError(uuid, 'missing \'disable\' function');
        return;
    }


The Shell listens for a GSettings changed signal that the org.gnome.shell.enabled_extensions key has changed and invokes onEnabledExtensionsChanged which gets the list of enabled Shell extensions, compares it to the current in-memory list and enables or disables Shell extensions accordingly by calling init followed by enable for any new Shell extension added to the whitelist, enable for Shell extensions previously initialized but disabled, and disable for Shell extensions that are no longer on the whitelist.

function disableExtension(uuid) {
    let meta = extensionMeta[uuid];
    if (!meta)
        return;

    if (meta.state != ExtensionState.ENABLED)
        return;

    let extensionState = extensionStateObjs[uuid];

    // "Rebase" the extension order by disabling and then enabling extensions
    // in order to help prevent conflicts.

    // Example:
    //   order = [A, B, C, D, E]
    //   user disables C
    //   this should: disable E, disable D, disable C, enable D, enable E

    let orderIdx = extensionOrder.indexOf(uuid);
    let order = extensionOrder.slice(orderIdx + 1);
    let orderReversed = order.slice().reverse();

    for (let i = 0; i < orderReversed.length; i++) {
        let uuid = orderReversed[i];
        try {
            extensionStateObjs[uuid].disable();
        } catch(e) {
            logExtensionError(uuid, e.toString());
        }
    }

    try {
        extensionState.disable();
    } catch(e) {
        logExtensionError(uuid, e.toString());
        return;
    }

    for (let i = 0; i < order.length; i++) {
        let uuid = order[i];
        try {
            extensionStateObjs[uuid].enable();
        } catch(e) {
            logExtensionError(uuid, e.toString());
        }
    }

    extensionOrder.splice(orderIdx, 1);

    meta.state = ExtensionState.DISABLED;
    _signals.emit('extension-state-changed', meta);
}

function enableExtension(uuid) {
    let meta = extensionMeta[uuid];
    if (!meta)
        return;

    if (meta.state == ExtensionState.INITIALIZED) {
        loadExtension(meta.dir, meta.type, true);
        return;
    }

    if (meta.state != ExtensionState.DISABLED)
        return;

    let extensionState = extensionStateObjs[uuid];

    extensionOrder.push(uuid);

    try {
        extensionState.enable();
    } catch(e) {
        logExtensionError(uuid, e.toString());
        return;
    }

    meta.state = ExtensionState.ENABLED;
    _signals.emit('extension-state-changed', meta);
}

function onEnabledExtensionsChanged() {
    let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);

    // Find and enable all the newly enabled extensions: UUIDs found in the
    // new setting, but not in the old one.
    newEnabledExtensions.filter(function(uuid) {
        return enabledExtensions.indexOf(uuid) == -1;
    }).forEach(function(uuid) {
        enableExtension(uuid);
    });

    // Find and disable all the newly disabled extensions: UUIDs found in the
    // old setting, but not in the new one.
    enabledExtensions.filter(function(item) {
        return newEnabledExtensions.indexOf(item) == -1;
    }).forEach(function(uuid) {
        disableExtension(uuid);
    });

    enabledExtensions = newEnabledExtensions;
}


Changes to the Shell UI by a Shell extension are controlled by whatever code is in the Shell extension’s enable and disable methods or functions. Typically, the Shell will call init, and then enable if the Shell extension is enabled, i.e. is in the Shell extension whitelist when the Shell starts up. The Shell may call disable and enable again during the same session. However, the Shell does not call enable or disable if a Shell extension is disabled at startup. Currently, the Shell calls init for all Shell extension including disabled Shell extensions at startup.

You can code your Shell extension so that init returns a JavaScript object with two methods called enable and disable. The object type returned does not matter. The Shell does not touch that object except to invoke either the enable or disable methods. If init does not return anything or returns a falsey (something which evaluates to FALSE) object, the Shell then assumes that enable and disable are functions defined within extension.js and acts accordingly.

Here is a modified version of the extension.js generated by the 3.2 version of gnome-shell-extension-tool in which init returns a simple object with enable and disable methods.

const Lang = imports.lang;
const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;

function HelloWorldExtension() {
    this._init();
}

HelloWorldExtension.prototype = {
    _init: function() {
        this.button = new St.Bin({ style_class: 'panel-button',
                                   reactive: true,
                                   can_focus: true,
                                   x_fill: true,
                                   y_fill: false,
                                   track_hover: true });
        this.text = null;
        let icon = new St.Icon({ icon_name: 'system-run',
                                 icon_type: St.IconType.SYMBOLIC,
                                 style_class: 'system-status-icon' });

        this.button.set_child(icon);
        this.button.connect('button-press-event', Lang.bind(this, this._showHello));
    },

    _hideHello: function() {
        Main.uiGroup.remove_actor(this.text);
        this.text = null;
    },

    _showHello: function() {
        if (!this.text) {
            this.text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
            Main.uiGroup.add_actor(this.text);
        }

        this.text.opacity = 255;

	let monitor = Main.layout.primaryMonitor;

        this.text.set_position(Math.floor(monitor.width / 2 - this.text.width / 2),
                               Math.floor(monitor.height / 2 - this.text.height / 2));

        Tweener.addTween(this.text,
                         { opacity: 0,
                           time: 2,
                           transition: 'easeOutQuad',
                           onComplete: Lang.bind(this, this._hideHello) });
    },

    enable: function() {
        Main.panel._rightBox.insert_actor(this.button, 0);
    },

    disable: function() {
        Main.panel._rightBox.remove_actor(this.button);
    }
};

function init() {
    return new HelloWorldExtension();
}


The key line is return new HelloWorldExtension. This is the initialized object that is passed back to the Shell. It contains the enable method which inserts the button on the panel and the disable method which removes the button from the panel.

From the above, it should be obvious that you can modify your 3.0 Shell extension in one of three ways to enable it to work with GNOME Shell 3.2.

  • BAD – You can rename the main function to init, and add empty enable and disable functions. The UI is displayed when the extension is loaded and cannot be enabled or disabled.
  • GOOD – You can rename the main function to init, and move any code that adds actors, or other UI components, to the Shell into a new enable function and add a new disable function that removes such UI components.
  • BETTER – You can rename the main function to init and modify the code in init to return an object that has enable and disable functions.

In theory, if you want your Shell extension to work with both GNOME 3.2 and GNOME 3.0, you could add a simple main function that calls init followed by enable. In practice, I have not found this to be workable unless the Shell extension is extremely simple because of the large number of differences in the names of internal variables and the workings of functions between GNOME 3.0 and GNOME 3.2.

For example, here is the GNOME 3.0 version of extension.js for my noa11y Shell extension. The purpose of that extension is to remove the accessibility (AKA a11y) button from the right-hand side of the panel.

const Panel = imports.ui.panel;

function main() {

    let index = Panel.STANDARD_TRAY_ICON_ORDER.indexOf('a11y');
    if (index >= 0) {
        Panel.STANDARD_TRAY_ICON_ORDER.splice(index, 1);
    }
    delete Panel.STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['a11y'];

}


As you can see it was a very simple extension. It simply blew away the code associated with a11y.

Here is the code for GNOME 3.2 version of the same Shell extension:

const Main = imports.ui.main;

function NoA11y() {
    this._init()
}

NoA11y.prototype = {
    _init: function() {
        this._removed = null;
    },

    enable: function() {
        if (this._removed == true) return;

        let _a11y = Main.panel._statusArea['a11y'];

        let children = Main.panel._rightBox.get_children();
        for (let i = 0; i < children.length; i++) {
            if (children[i]._delegate == _a11y) {
                children[i].destroy();
                this._removed = true;
                Main.panel._statusArea['a11y'] = null;
                break;
            }
        }
    },


    disable: function() {
          if (this._removed == false) return;
          
          let _index = 0;
          let _volume = Main.panel._statusArea['volume'];

          let children = Main.panel._rightBox.get_children();
          for (let i = 0; i < children.length; i++) {
              if (children[i]._delegate == _volume) { 
                  _index = i;
                  break;
              }
          }
          if (_index > 0) _index--;

          let indicator = new Main.panel._status_area_shell_implementation['a11y'];
          Main.panel.addToStatusArea('a11y', indicator, _index);
          this._removed = false;
    }

};


function init() {
    return new NoA11y(); 
}


Most of the new code is in support of the enable and disable extension UI functionality. As you can see, supporting such functionality significantly increases the number of lines of code and complexity of the extension. I could easily make a valid argument that you need even more detailed knowledge of the internals of the 3.2 Shell than you needed for the 3.0 Shell to write Shell extensions that support enable and disable functionality.

Here is how the Linux Mint developers implemented their noa11y Shell extension:

const Main = imports.ui.main;
const Panel = imports.ui.panel;

let indicator;
let idx = null;

function init(extensionMeta) {
    indicator = new Panel.STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION["a11y"];
}

function enable() {
    // Remove A11Y menu
    for (let i = 0; i < Main.panel._rightBox.get_children().length; i++) {
        if (Main.panel._statusArea['a11y'] == Main.panel._rightBox.get_children()[i]._delegate) {
            Main.panel._rightBox.get_children()[i].destroy();
            break;
        }
    }
    // addToStatusArea would throw an error on disable if we don't set this to null
    Main.panel._statusArea['a11y'] = null;    
}

function disable() {
   Main.panel.addToStatusArea("a11y", indicator, idx);
}


By the way, their MGSE (Mint GNOME Shell Extensions) repository is well worth checking out.

Monkey patching does not work the same in the 3.2 Shell as it did in the 3.0 Shell. Consider the differences between the 3.0 version of my Shell extension that replaces the SYMBOLIC icons on the panel right-hand side with FULLCOLOR icons (much nicer!)

Here is the contents of the 3.0 extension.js:

 
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;


function main() {

    // monkey patch
    PanelMenu.SystemStatusButton.prototype._init = function(iconName, tooltipText) {
        PanelMenu.Button.prototype._init.call(this, 0.0);
        this._iconActor = new St.Icon({ icon_name: iconName,
                                        icon_type: St.IconType.FULLCOLOR,
                                        style_class: 'system-status-icon' });
        this.actor.set_child(this._iconActor);
        this.actor.set_style_class_name('panel-status-button');
        this.setTooltip(tooltipText);
    };

}


and here is what the 3.2 extension.js contains:

const Main = imports.ui.main;
const St = imports.gi.St;


function ColorStatusButtonsExtension() {
    this._init()
}


ColorStatusButtonsExtension.prototype = {
    _init: function() {
        this._fullcolor = null;
    },

    disable: function() {
        let children = Main.panel._rightBox.get_children();
        for (let i = 0; i < children.length; i++) {
            if (children[i] && children[i]._delegate._iconActor) {
                children[i]._delegate._iconActor.icon_type = St.IconType.SYMBOLIC;
                children[i]._delegate._iconActor.style_class = 'system-status-icon';
            }
        }
    },

    enable: function() {
        let children = Main.panel._rightBox.get_children();
        for (let i = 0; i < children.length; i++) {
            if (children[i] && children[i]._delegate._iconActor) {
                children[i]._delegate._iconActor.icon_type = St.IconType.FULLCOLOR;
                children[i]._delegate._iconActor.style_class = 'color-status-button';
            }
        }
    }
};


function init() {
    return new ColorStatusButtonsExtension(); 
}


Not all of the work that you need to do to enable your Shell Extension to work with GNOME 3.2 is in extension.js. You also need to modify metadata.json to match the required version of the GNOME Shell (shell-version) and possibly also the GJS version (js-version). See the versionCheck function in extensionSystem.js. To find out the version of GNOME Shell that you are running, enter gnome-shell –version, or cat /usr/share/gnome-shell/js/misc/config.js. Currently, I set the shell-version to 3.2 in metadata.json.

By the way, if you run into trouble getting your extension to work, try the following command to invoke the GNOME Shell in debug mode:

env GJS_DEBUG_OUTPUT=stderr  gnome-shell --replace


Also remember that Looking Glass is your friend. Get familiar with Looking Glass and you can use it to solve many a coding error. Note however that only the properties and methods of introspected objects that have been used before LG is invoked will show up.

It is nice to see that the Shell extensions got a modicum of real love in GNOME 3.2. Remember that it is not so long ago that some people were adamant that Shell extensions and themes were an evil aberration, were detracting from the purity of the one single cross-distribution GNOME Shell experience, and should be totally unsupported! With the release of GNOME 3.2, this is no longer the case. The user base won the battle. Hundreds of extensions, some useful, some quirky, had been developed since the release of GNOME 3.0 earlier this year.

29 comments to Updating GNOME Shell Extensions To Work With GNOME 3.2

  • Charles Bowman

    Very informative as usual.

    Thanks Finn.

  • wnfu12nb

    how can I modify the autohidetopbar extension to show the topbar only in overview mode?

  • Albert

    Great post :-). BTW, why do you say that returning an object with enable and disable methods is best?

    Also, it is worth noting that a patch has already been committed to make it so disabled extenstions’ init methods are not called, so we can look forward to that for 3.4 :-).

    • I like to encapsulate enable() and disable() simply because I believe that it is a better way to code JavaScript.

      Yes I am aware of that particular change in G S 3.4 and have been experimenting with it. Lots more will break in extensions by the time 3.4 is released.

  • Charles Bowman

    Noticed something interesting yesterday.

    My expectation is that disabled extensions don’t effect the other extensions but when checking out the movehotcorner extension with the MGSE extensions the right hot corner is active & set disabled. If I enable it I get an overview icon “button” & I have to click on it, disaled I get that nice bump the corner to show overview behavior.

    During the early testing I would activate/deactivate extensions by compressing them & deleting the directory. This recent experience indicates that if the extension exists it does load (as indicated by lg) & in some cases still affects the desktop even when disabled.

  • please. I need the extension autohidetopbar, I’m from south america, my english is not very good. I wish express to the extension is the most important for me because my monitor is really small, thanks for listening and notify to me the actualization.

  • Steven Webb

    Thanks for all of your great work Finnbarr.

    For anyone interested, I’ve made some small modifications and improvements to the move hot corner extension (https://github.com/swebb/Move-Hot-Corner).

    It would be nice to have a more comprehensive solution that can add hot spots on multiple monitors; however, I haven’t been able to find any documentation about available api’s.

  • robytrevi

    Hi.
    I’m using the colorstatusbutton extension in Ubuntu Oneiric (started from a minimal installation).
    When I activate it the size of the icons is enormous.
    How can I fix it?
    The simply modification of “icon-size: 1.34em;” in stylesheet.css file doesn’t work (nothing happen also after the refresh of the shell).

    • Charles Bowman

      Have a you tried it with a different icon set, or did you find a solution?

  • Slawek

    Great, now I have to learn Java Script.

  • Hi, Activities Button doesn’t work with Gnome-Shell Faience (http://tiheum.deviantart.com/art/Gnome-Shell-Faience-255097456)
    If I enable the extension, it will change my shell theme to Default
    Could you fix it, please :-)

    Thank you.

    • Lucius,

      You have not provided enough information to help you. Which version of GNOME Shell are you using on which platform?

      • Lucius

        Oh, I am sorry, I’m using Gnome Shell 3.2.1 and Fedora 16

        Thank you so much :-)

  • robytrevi

    There are some graphic incompatibility between activitiesbutton extension, colorsbutton extension and the mgse menu of webupd8 group: http://www.webupd8.org/2011/11/try-new-mint-gnome-shell-extensions.html (look at my post at the end.)

    • robytrevi

      The problem is the interaction between line 19 of extension.js file of activitiesbutton extnsion:
      this._themeContext = St.ThemeContext.get_for_stage(global.stage);
      and the same file of mgse extension at line 221 222:
      let focusManager = St.FocusManager.get_for_stage(global.stage);
      let group = focusManager.get_group(this.actor);
      But I really don’t understand javascript yet.

    • Charles Bowman

      Not surprised, the MGSE set is a nice package but there was no thought or testing with other extensions or themes from the look of things.

  • BanjoKU

    Hi,

    In your Hello World, the status icon that you add to the panel is incorrectly spaced, If you look at the space between other icons, yours is too close to its neighbour.

    I assume this can be altered by adding your own CSS, but is there another way to get correct spacing as hardcoding on a per icon basis is not ideal if you want to be compatible with all shell themes.

  • Thank you! I have (ab)used your idea to make a little extension (see in my blog) to change the font size and padding in the top panel. It’s a shameless copy :-), so I haven’t published it in extensions.gnome.org — unless the original author authorizes it.

  • Weakmutation

    Hello, the information about GTK3 theming is pretty scarce so I thought of asking here:
    1
    Why don’t custom themes that use unico load colors properly, especially dark variations? The common symptom are wrong selection color or gray font with darkish themes. The only workaround that works for me is logging out of gnome and back in
    2
    Is adwaita engine really limited in its graphical capabilities compared to unico? Or are the differences minor?
    3
    Is theme test available in default Ubuntu Oneiric repositories?

  • Marshall Neill

    Hi,
    Allow me to introduce myself, I am Marshall Neill and recently installed you extension. Great, fantastic, just what I was looking for. I use it often and find it quite good.
    I have one question, “How do you or can you change the popup of the different menu items?”
    When moving from one category to the next, if you move the mouse over another category it immediately opens. I have been searching your code and found no time delay or timeout function. If it had just about 1/2 second delay for showing the next category that would be excellent. The way it is now I constantly have to go back and insure that I move the mouse horizontally to the menu item I want.
    Again, your extension is great and thanks for all the work you have put into it.
    I am beginning to see more and more extensions and it seems kinda funny to me that what I sorta wound up with was a Gnome 2 look within a Gnome 3 environment. The extensions make Gnome 3 that much better and, to me, much more usable.
    Thank you again.
    Marshall Neill

  • didou

    Hi,

    I’m new to extensions development, and I find it quiet tedious to start! Thanks for this blog post which helps a lot.
    I’ve started my first extension, and would like to know if there’s any other way to update it than reloading all gnome-shell everytime ? (gnome-shell –replace)

    Thanks !

  • Isaac

    Hello,

    First of all, thanks for these guides! I’ve wanted to write an extension since I started using Gnome 3 but there didn’t seem to be any good documentation on it.

    I’m confused as to why you want to have init return a JavaScript object as opposed to simply using three separate functions. For one thing, it’s more confusing and less straightforward then the standard code generated. Also, it doesn’t work on my Fedora 16 (Gnome 3.2.1) system. The “Hello, world!” box appears up in the top left corner, making me believe the “this.text.set_position” line is failing. The box also doesn’t fade. I’ve tried poking around but it doesn’t seem to work no matter what I do.

    Thanks,
    Isaac

    • Either way of coding works. I choose to code in an object-orientated manner. You do not – and that is fine. If you read the JavaScript sourcecode for the GNOME Shell, you will see that it is mostly written as object-orientated code. For more information on OO JavaScript, I suggest you read “JavaScript: The Good Parts” by Douglas Crockford.

      As for you problems with your helloworld example, which helloworld example are you using? I just checked the helloworld exention at http://fpmurphy.com/gnome-shell-extensions and it is working as expected. The message appears in the middle of my screen and fades as expected. Without more details, there is not much I can do to help you.

  • Hi!
    Are you planning to do a similar guide to help us migrating our extensions from 3.2 to 3.4?

    It will be very useful!

    I’m gettin a continued “extension is not compatible with current GNOME Shell and/or GJS version” and I don’t understand why.
    Also, do you know where I can find some “the old VS the new” guidelines to write 3.4-compatible extensions?

    Many thanks.

  • hermes14

    Hi,
    I installed the gnote extension, but it doesn’t work. Looking glass reports this error:
    TypeError: _icon is undefined

    Running gnome-shell 3.2.1 on Arch Linux. Any hint?
    Thanks!