I’d like to create a minimal extension that has a simple toggle button without menu. I simply want the icon that appears in the gnome panel to directly be a toggle button.
I found plenty of examples involving toggle buttons within submenus, but I can’t figure out how to register a click handler on the icon directly.
Any ideas?
const { GObject, St } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const _ = ExtensionUtils.gettext;
const Indicator = GObject.registerClass(
class Indicator extends PanelMenu.Button {
_init() {
super._init(0.0, _('My Shiny Indicator'));
const enabledIcon = new St.Icon({
icon_name: 'face-smile-symbolic',
style_class: 'system-status-icon',
});
const disabledIcon = new St.Icon({
icon_name: 'face-sad-symbolic',
style_class: 'system-status-icon',
});
this.add_child(enabledIcon);
// How can I do soemthing like this?
let toggleStatus = true
function onIconClick() {
toggleStatus = !toggleStatus
if (toggleStatus) {
Main.notify('Toggle has been turned on!');
this.remove_child(disabledIcon);
this.add_child(enabledIcon);
} else {
Main.notify('Toggle has been turned off!');
this.remove_child(enabledIcon);
this.add_child(disabledIcon);
}
Main.notify('The new toggle status is: ' + toggleStatus);
}
enabledIcon.on('click', onIconClick)
disabledIcon.on('click', onIconClick)
}
});
class Extension {
constructor(uuid) {
this._uuid = uuid;
}
enable() {
this._indicator = new Indicator();
Main.panel.addToStatusArea(this._uuid, this._indicator);
}
disable() {
this._indicator.destroy();
this._indicator = null;
}
}
function init(meta) {
return new Extension(meta.uuid);
}
_init () {
super._init(0.0, _('My Shiny Indicator'));
this._icon = new St.Icon({
icon_name: 'face-smile-symbolic',
style_class: 'system-status-icon',
});
this.add_child(this._icon);
// Connect to signals using GObject.Object.connect(), use
// Function.bind() on the callback so you can use `this`
this.connect('event', this._onClicked.bind(this));
// Alternatively, using an arrow function will allow the same
this.connect('event', (actor, event) => {
// etc
});
}
_onClicked(actor, event) {
// Some other non-clicky event happened; bail
if ((event.type() !== Clutter.EventType.TOUCH_BEGIN &&
event.type() !== Clutter.EventType.BUTTON_PRESS))
return Clutter.EVENT_PROPAGATE;
// Might as well check the toggled state based on the icon-name
if (this._icon.icon_name === 'face-smile-symbolic') {
this._icon.icon_name = 'face-sad-symbolic';
Main.notify('Toggle has been turned off!');
} else {
this._icon.icon_name = 'face-smile-symbolic';
Main.notify('Toggle has been turned on!');
}
// Propagate the event
return Clutter.EVENT_PROPAGATE;
}
Thank you, that works like a charm! The link to the documentation is really helpful as well. I’ve been searching through the source code on Gitlab the whole time and couldn’t make sense of the class hierarchy.
Edit:
In case anyone needs it, here is a full code example:
// Simple Toogle Button Extension
const { Clutter, GObject, St } = imports.gi;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const TOGGLE_ON_ICON = 'face-smile-symbolic';
const TOGGLE_OFF_ICON = 'face-sad-symbolic';
const Indicator = GObject.registerClass(
class Indicator extends PanelMenu.Button {
_init () {
super._init(0.0, 'Toggle Button');
this._icon = new St.Icon({
icon_name: 'face-smile-symbolic',
style_class: 'system-status-icon',
});
this.add_child(this._icon);
this.connect('event', this._onClicked.bind(this));
}
_onClicked(actor, event) {
if ((event.type() !== Clutter.EventType.TOUCH_BEGIN && event.type() !== Clutter.EventType.BUTTON_PRESS)) {
// Some other non-clicky event happened; bail
return Clutter.EVENT_PROPAGATE;
}
if (this._icon.icon_name === TOGGLE_ON_ICON) {
this._icon.icon_name = TOGGLE_OFF_ICON;
Main.notify('Toggle has been turned off!');
} else {
this._icon.icon_name = TOGGLE_ON_ICON;
Main.notify('Toggle has been turned on!');
}
return Clutter.EVENT_PROPAGATE;
}
});
class Extension {
constructor(uuid) {
this._uuid = uuid;
}
enable() {
this._indicator = new Indicator();
Main.panel.addToStatusArea(this._uuid, this._indicator);
}
disable() {
this._indicator.destroy();
this._indicator = null;
}
}
function init(meta) {
return new Extension(meta.uuid);
}