diff --git a/README.md b/README.md index bc0ddbc..db55fdc 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Example You can test it with command line : - gdbus call --session --dest org.gnome.Shell --object-path /com/soutade/GenericMonitor --method com.soutade.GenericMonitor.notify '{"group":"new","items":[{"name":"first","text":"Hello","style":"color:green","popup":{"items":{"picture":{"path":"/tmp/cat2.jpg"}}}}]}' + gdbus call --session --dest org.gnome.Shell --object-path /com/soutade/GenericMonitor --method com.soutade.GenericMonitor.notify '{"group":"new","items":[{"name":"first","on-enter":"open-popup","on-leave":"close-popup","text":"Hello","style":"color:green","popup":{"items":[{"picture":{"path":"/tmp/cat2.jpg"}}]}}]}' gdbus call --session --dest org.gnome.Shell --object-path /com/soutade/GenericMonitor --method com.soutade.GenericMonitor.deleteGroups '{"groups":["new"]}' diff --git a/extension.js b/extension.js index b467e2d..1770570 100644 --- a/extension.js +++ b/extension.js @@ -22,7 +22,7 @@ // https://gjs-docs.gnome.org/st10~1.0_api/st.widget // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/panelMenu.js // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/popupMenu.js -//https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/panel.js +// https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/panel.js const St = imports.gi.St; const Gio = imports.gi.Gio; const Lang = imports.lang; @@ -36,14 +36,17 @@ const GObject = imports.gi.GObject; const Pixbuf = imports.gi.GdkPixbuf; const Cogl = imports.gi.Cogl; -function hash_get(hash, key, default_value) -{ +function hash_get(hash, key, default_value) { if (hash.hasOwnProperty(key)) return hash[key]; return default_value; } +function log(message) { + global.log('[GenericMontior]', message); +} + var MyPopupMenuItem = GObject.registerClass({ GTypeName: "MyPopupMenuItem" }, @@ -68,20 +71,24 @@ var MonitorWidget = GObject.registerClass({ class MonitorWidget extends PanelMenu.Button { - _init(item, group, position) { + _init(item, group, dbus, position) { super._init(0.0); this.name = item['name']; this.group = group; this.fullname = this.name + '@' + this.group; - + this.dbus = dbus; + if (item.hasOwnProperty('icon')) { if (typeof(item['icon']) === "string") this.icon = this._createIconOld(item); else this.icon = this._createIcon(item['icon']); - this.add_child(this.icon); + if (this.icon !== null) { + this._connectWidgetSignals(this.icon); + this.add_child(this.icon); + } } else this.icon = null; @@ -92,7 +99,11 @@ class MonitorWidget extends PanelMenu.Button { this.widget = this._createTextOld(item); else this.widget = this._createText(item['text']); - this.add_child(this.widget); + + if (this.widget !== null) { + this._connectWidgetSignals(this.widget); + this.add_child(this.widget); + } } else this.widget = null; @@ -106,10 +117,20 @@ class MonitorWidget extends PanelMenu.Button { let box = hash_get(item, 'box', 'center'); + if (box === 'right' && position == -1) + position = 0; + this.connect('enter-event', this._onEnter.bind(this)); this.connect('leave-event', this._onLeave.bind(this)); this.connect('style-changed', this._onStyleChanged.bind(this)); + + // Disable click event at PanelMenu.button level + this.setSensitive(false); + this.nbClicks = 0; + this.button = -1; + this.nbEnter = 0; + Main.panel.addToStatusArea(this.fullname, this, position, box); } @@ -119,31 +140,28 @@ class MonitorWidget extends PanelMenu.Button { this._natHPadding = 1; } - _onEnter() { - this.menu.open(true); + _connectWidgetSignals(widget) { + widget.connect('enter-event', this._onEnter.bind(this)); + widget.connect('leave-event', this._onLeave.bind(this)); + widget.set_reactive(true); + widget.connect('button-release-event', Lang.bind(this, this._clicked)); } - - _onLeave() { - this.menu.close(); - } - + _createPopup(item) { if (!item.hasOwnProperty('items')) { return null; } let widgets = []; - let widget; for (let itemIndex in item['items']) { - switch(itemIndex) - { - case 'text': - widget = this._createText(item['items'][itemIndex]); - break; - case 'picture': - widget = this._createPicture(item['items'][itemIndex]); - break - } + let widget = null; + let widgetDict = item['items'][itemIndex]; + + if (widgetDict.hasOwnProperty('text')) + widget = this._createText(widgetDict['text']); + else if (widgetDict.hasOwnProperty('picture')) + widget = this._createPicture(widgetDict['picture']); + if (widget !== null) widgets.push(widget); } @@ -151,7 +169,7 @@ class MonitorWidget extends PanelMenu.Button { if (widgets.length > 0) { this.menuItem = new MyPopupMenuItem(widgets, {}); this.menu.addMenuItem(this.menuItem); - + this.menu.setSensitive(false); return this.menuItem; } @@ -167,8 +185,10 @@ class MonitorWidget extends PanelMenu.Button { } _createText(item) { - if (!item.hasOwnProperty('text')) - throw new Error("Text must have a \'text\' value"); + if (!item.hasOwnProperty('text')) { + log("Text must have a \'text\' value"); + return null; + } let style = hash_get(item, 'style', ''); @@ -179,6 +199,7 @@ class MonitorWidget extends PanelMenu.Button { } else { let widget = new St.Button({ label: item['text'] }); widget.set_style(style); + return widget; } } @@ -192,8 +213,10 @@ class MonitorWidget extends PanelMenu.Button { } _createIcon(item) { - if (!item.hasOwnProperty('path')) - throw new Error("Icon must have a \'path\' value"); + if (!item.hasOwnProperty('path')) { + log("Icon must have a \'path\' value"); + return null; + } let style = hash_get(item, 'style', ''); @@ -205,13 +228,16 @@ class MonitorWidget extends PanelMenu.Button { let gicon = Gio.icon_new_for_string(item['path']); gicon = new St.Icon({ gicon }); gicon.set_style(style); + return gicon; } } _createPicture(item) { - if (!item.hasOwnProperty('path')) - throw new Error("picture must have a \'path\' value"); + if (!item.hasOwnProperty('path')) { + log("Picture must have a \'path\' value"); + return null; + } let width = hash_get(item, 'width', -1); let height = hash_get(item, 'height', -1); @@ -238,11 +264,77 @@ class MonitorWidget extends PanelMenu.Button { return picture; } - removeFromBox() { - if (this.widget) - this.box.remove_child(this.widget); - if (this.icon) - this.box.remove_child(this.icon); + _manageEventAction(action) { + if (action === 'open-popup') + this.menu.open(true); + else if (action === 'close-popup') + this.menu.close(); + else if (action == 'delete') + this.dbus.deleteItem(this, this.group); + else + return Clutter.EVENT_PROPAGATE; + return Clutter.EVENT_STOP; + } + + _manageLeaveEvent() { + if (this.nbEnter <= 0) { + this._manageEventAction(this.onLeave); + this.nbEnter = 0; + } + } + + _doClickCallback() { + let right = ''; + let nbClicks = ''; + if (this.button == 3) + right = 'Right'; + if (this.nbClicks > 1) + nbClicks = 'Dbl'; + let signalName = 'on' + right + nbClicks + 'Click'; + this.dbus.emit_signal(signalName, this.fullname); + this.nbClicks = 0; + this.button = -1; + } + + _clicked(actor, event) { + if (this.onClick === 'signal') { + if (event.get_button() == this.button) { + this.nbClicks++; + } else { + this.button = event.get_button(); + this.nbClicks = 1; + + Mainloop.timeout_add(this.dbus.ClutterSettings['double-click-time'], + Lang.bind(this, this._doClickCallback)); + } + return Clutter.EVENT_STOP; + } else if (this.onClick === 'open-popup') { + this.menu.toggle(); + } else + return this._manageEventAction(this.onClick); + } + + _onEnter() { + if (this.onEnter === 'signal') + this.dbus.emit_signal('onEnter', this.fullname); + else { + this.nbEnter++; + return this._manageEventAction(this.onEnter); + } + + return Clutter.EVENT_STOP; + } + + _onLeave() { + if (this.onLeave === 'signal') + this.dbus.emit_signal('onLeave', this.fullname); + else { + this.nbEnter--; + Mainloop.timeout_add(this.dbus.ClutterSettings['double-click-time'], + Lang.bind(this, this._manageLeaveEvent)); + } + + return Clutter.EVENT_STOP; } update(item) { @@ -363,17 +455,27 @@ class GenericMonitorDBUS { this._dbusImpl.emit_signal('onActivate', null); } - _checkParmeters(parameters) { - if (!parameters.hasOwnProperty('group')) - throw new Error('No group defined'); + emit_signal(name, value) { + this._dbusImpl.emit_signal(name, GLib.Variant.new('(s)',[value])); + } - if (!parameters.hasOwnProperty('items')) - throw new Error('No items defined'); + _checkParameters(parameters) { + if (!parameters.hasOwnProperty('group')) { + log('No group defined'); + return false; + } + + if (!parameters.hasOwnProperty('items')) { + log('No items defined'); + return false; + } for (let itemIndex in parameters['items']) { let item = parameters['items'][itemIndex]; - if (!item.hasOwnProperty('name')) - throw new Error('No name defined for item'); + if (!item.hasOwnProperty('name')) { + log('No name defined for item'); + return false; + } // if (!item.hasOwnProperty('text') && !item.hasOwnProperty('icon')) // throw new Error('No text not icon defined for item'); // if (item.hasOwnProperty('on-click')) { @@ -385,6 +487,8 @@ class GenericMonitorDBUS { // throw new Error('Invalid box value'); // } } + + return true; } _getItemFromGroup(group, name) { @@ -466,7 +570,8 @@ class GenericMonitorDBUS { notify(str) { let parameters = JSON.parse(str); - this._checkParmeters(parameters); + if (!this._checkParameters(parameters)) + return; let groupName = parameters['group']; let group; @@ -498,7 +603,7 @@ class GenericMonitorDBUS { if (position >= childrens.length) position = -1; } - monitorWidget = new MonitorWidget(item, groupName, position); + monitorWidget = new MonitorWidget(item, groupName, this, position); group.push(monitorWidget); // Connect signals // if (onClick !== '') { @@ -522,8 +627,8 @@ class GenericMonitorDBUS { deleteItem(item, groupName) { let group = this.monitor_groups[groupName]; - item.removeFromBox(); group = this._removeFromArray(group, item); + item.destroy(); if (group.length === 0) delete this.monitor_groups[groupName]; else @@ -533,14 +638,18 @@ class GenericMonitorDBUS { deleteItems(str) { let parameters = JSON.parse(str); - if (!parameters.hasOwnProperty('items')) - throw new Error('No items defined'); + if (!parameters.hasOwnProperty('items')) { + log('No items defined'); + return false; + } for (let itemIndex in parameters['items']) { let itemName = parameters['items'][itemIndex]; let fullName = itemName.split('@'); - if (fullName.length !== 2) - throw new Error(`Invalid name ${itemName}`); + if (fullName.length !== 2) { + log(`Invalid name ${itemName}`); + return false; + } itemName = fullName[0]; let groupName = fullName[1]; if (!this.monitor_groups.hasOwnProperty(groupName)) @@ -556,8 +665,10 @@ class GenericMonitorDBUS { deleteGroups(str) { let parameters = JSON.parse(str); - if (!parameters.hasOwnProperty('groups')) - throw new Error('No groups defined'); + if (!parameters.hasOwnProperty('groups')) { + log('No groups defined'); + return false; + } let groupsToDelete = []; for (let groupIndex in parameters['groups']) { @@ -566,7 +677,7 @@ class GenericMonitorDBUS { continue; let group = this.monitor_groups[groupName]; for (let itemIndex in group) - group[itemIndex].removeFromBox(); + group[itemIndex].destroy(); groupsToDelete.push(groupName); } for (let groupDeleteIndex in groupsToDelete) { @@ -580,7 +691,7 @@ class GenericMonitorDBUS { for (let groupIndex in this.monitor_groups) { let group = this.monitor_groups[groupIndex]; for (let itemIndex in group) - group[itemIndex].removeFromBox(); + group[itemIndex].destroy(); } this.monitor_groups = {}; this._dbusImpl.unexport();