From 5458908603ac0b87109aed6e8ffc8862b3fbc0ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Tue, 3 Nov 2020 16:59:27 +0100 Subject: [PATCH] New architecture : create & update works, signals and remove doesn't --- README.md | 3 +- extension.js | 373 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 219 insertions(+), 157 deletions(-) diff --git a/README.md b/README.md index 3bc21b0..bc0ddbc 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,8 @@ 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"}]}' + 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.deleteGroups '{"groups":["new"]}' Python example is available diff --git a/extension.js b/extension.js index 0100b09..71b5ded 100644 --- a/extension.js +++ b/extension.js @@ -20,82 +20,47 @@ // https://github.com/bananenfisch/RecentItems/blob/master/extension.js // https://github.com/julio641742/gnome-shell-extension-reference/blob/master/tutorials/POPUPMENU-EXTENSION.md // 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 const St = imports.gi.St; const Gio = imports.gi.Gio; const Lang = imports.lang; const GLib = imports.gi.GLib const Main = imports.ui.main; const Mainloop = imports.mainloop; -const clutter = imports.gi.Clutter; +const Clutter = imports.gi.Clutter; const PanelMenu = imports.ui.panelMenu; const PopupMenu = imports.ui.popupMenu; const GObject = imports.gi.GObject; -const Gtk = imports.gi.Gtk; const Pixbuf = imports.gi.GdkPixbuf; const Cogl = imports.gi.Cogl; +function hash_get(hash, key, default_value) +{ + if (hash.hasOwnProperty(key)) + return hash[key]; + + return default_value; +} + var MyPopupMenuItem = GObject.registerClass({ GTypeName: "MyPopupMenuItem" }, class MyPopupMenuItem extends PopupMenu.PopupBaseMenuItem { - _init(gicon, text, params) { - super._init(params); + _init(widgets, params) { + super._init(params); - this.box = new St.BoxLayout({ style_class: 'popup-combobox-item' }); - this.box.set_vertical(true); - this._img = new clutter.Image(); - let initial_pixbuf = Pixbuf.Pixbuf.new_from_file('/tmp/cat2.jpg'); - this._img.set_data(initial_pixbuf.get_pixels(), - initial_pixbuf.get_has_alpha() ? Cogl.PixelFormat.RGBA_8888 - : Cogl.PixelFormat.RGB_888, - initial_pixbuf.get_width(), - initial_pixbuf.get_height(), - initial_pixbuf.get_rowstride()); - this._icon = new clutter.Actor(); - this._icon.set_content(this._img); - this._icon.set_size(150,//initial_pixbuf.get_width(), - 300);//initial_pixbuf.get_height()); - this.box.add(this._icon); - - // if (gicon) - // this.icon = new St.Icon({ gicon: gicon, style_class: 'popup-menu-icon' }); - // else - // this.icon = new St.Icon({ icon_name: 'edit-clear-symbolic', icon_size: 22 }); + this.box = new St.BoxLayout({ style_class: 'popup-combobox-item' }); + this.box.set_vertical(true); - // this.box.add(this._icon); - this.label = new St.Label({ text: " " + text }); - this.box.add(this.label); - this.add_child(this.box); - } + for (let widgetIndex in widgets) + this.box.add(widgets[widgetIndex]); + + this.add_child(this.box); + } }); -var RecentItems = GObject.registerClass({ - GTypeName: "RecentItems" -}, -class RecentItems extends PanelMenu.Button { - _init() { - super._init(0.0); - this.connect('enter-event', this._onEnter.bind(this)); - this.connect('leave-event', this._onLeave.bind(this)); - this._iconActor = new St.Icon({ icon_name: 'document-open-recent-symbolic', style_class: 'system-status-icon' }); - this.actor.add_actor(this._iconActor); - this.actor.add_style_class_name('panel-status-button'); - Main.panel.addToStatusArea('recent-items', this); - - let menuItem = new MyPopupMenuItem('', 'hello', {}); - this.menu.addMenuItem(menuItem); - } - - _onEnter() { - this.menu.open(true); - } - - _onLeave() { - this.menu.close(); - } -}); - var MonitorWidget = GObject.registerClass({ GTypeName: "MonitorWidget" }, @@ -109,7 +74,10 @@ class MonitorWidget extends PanelMenu.Button { if (item.hasOwnProperty('icon')) { - this.icon = this._createIcon(icon, iconStyle); + if (typeof(item['icon']) === "string") + this.icon = this._createIconOld(item); + else + this.icon = this._createIcon(item['icon']); this.add_child(this.icon); } else @@ -117,7 +85,10 @@ class MonitorWidget extends PanelMenu.Button { if (item.hasOwnProperty('text')) { - this.widget = this._createText(text, style); + if (typeof(item['text']) === "string") + this.widget = this._createTextOld(item); + else + this.widget = this._createText(item['text']); this.add_child(this.widget); } else @@ -126,36 +97,12 @@ class MonitorWidget extends PanelMenu.Button { if (item.hasOwnProperty('popup')) this._createPopup(item['popup']); - if (item.hasOwnProperty('on-click')) - onClick = item['on-click']; - let box = 'center'; - if (item.hasOwnProperty('box')) - box = item['box']; + this.onClick = hash_get(item, 'on-click', ''); + this.onEnter = hash_get(item, 'on-enter', ''); + this.onLeave = hash_get(item, 'on-leave', ''); - this.onClick = onClick; - - switch(box) { - case 'left': - this.box = Main.panel._leftBox; - break; - case 'right': - this.box = Main.panel._rigthBox; - break; - default: - case 'center': - this.box = Main.panel._centerBox; - break; - } + let box = hash_get(item, 'box', 'center'); - // Don't know why, _rightBox seems undefined on shell 3.36 !! - if (this.box === undefined) { - log(`${box} box is undefined, falling back to center one`); - this.box = Main.panel._centerBox; - } - - let menuItem = new MyPopupMenuItem('', 'hello', {}); - this.menu.addMenuItem(menuItem); - this.connect('enter-event', this._onEnter.bind(this)); this.connect('leave-event', this._onLeave.bind(this)); @@ -170,29 +117,117 @@ class MonitorWidget extends PanelMenu.Button { this.menu.close(); } - _createText(text, style) { - this.style = style; - if (text === '') { + _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 + } + if (widget !== null) + widgets.push(widget); + } + + if (widgets.length > 0) { + this.menuItem = new MyPopupMenuItem(widgets, {}); + this.menu.addMenuItem(this.menuItem); + + return this.menuItem; + } + + return null; + } + + _createTextOld(item) { + var item_values = {}; + item_values = {'text':item['text']}; + if (item.hasOwnProperty('style')) + item_values['style'] = item['style']; + return this._createText(item_values); + } + + _createText(item) { + if (!item.hasOwnProperty('text')) + throw new Error("Text must have a \'text\' value"); + + let style = hash_get(item, 'style', ''); + + this.textProperties = item; + + if (item['text'] === '') { return null; } else { - widget = new St.Button({ label: text }); - widget.set_style(this.style); + let widget = new St.Button({ label: item['text'] }); + widget.set_style(style); return widget; } } - _createIcon(icon, style) { - this.iconStyle = style; - if (icon === '') { + _createIconOld(item) { + var item_values = {}; + item_values = {'path':item['icon']}; + if (item.hasOwnProperty('iconStyle')) + item_values['style'] = item['iconStyle']; + return this._createIcon(item_values); + } + + _createIcon(item) { + if (!item.hasOwnProperty('path')) + throw new Error("Icon must have a \'path\' value"); + + let style = hash_get(item, 'style', ''); + + this.iconProperties = item; + + if (item['path'] === '') { return null; } else { - let gicon = Gio.icon_new_for_string(icon); + let gicon = Gio.icon_new_for_string(item['path']); gicon = new St.Icon({ gicon }); - gicon.set_style(this.iconStyle); + gicon.set_style(style); return gicon; } } + _createPicture(item) { + if (!item.hasOwnProperty('path')) + throw new Error("picture must have a \'path\' value"); + + let width = hash_get(item, 'width', -1); + let height = hash_get(item, 'height', -1); + + if (typeof(width) === "string") + width = parseInt(width, 10); + + if (typeof(heigth) === "string") + heigth = parseInt(heigth, 10); + + let img = new Clutter.Image(); + let initial_pixbuf = Pixbuf.Pixbuf.new_from_file(item['path']); + img.set_data(initial_pixbuf.get_pixels(), + initial_pixbuf.get_has_alpha() ? Cogl.PixelFormat.RGBA_8888 + : Cogl.PixelFormat.RGB_888, + initial_pixbuf.get_width(), + initial_pixbuf.get_height(), + initial_pixbuf.get_rowstride()); + let picture = new Clutter.Actor(); + picture.set_content(img); + picture.set_size((width != -1)?width:initial_pixbuf.get_width(), + (height != -1)?height:initial_pixbuf.get_height()); + + return picture; + } + removeFromBox() { if (this.widget) this.box.remove_child(this.widget); @@ -200,39 +235,85 @@ class MonitorWidget extends PanelMenu.Button { this.box.remove_child(this.icon); } - update(text, style, icon, iconStyle) { + update(item) { let prevWidget = this.widget; let prevIcon = this.icon; - - if (text !== '') { - if (!this.widget) { - this._createText(text, style); - this.box.insert_child_above(this.widget, this.icon); - } else { - this.widget.label = text; + + if (item.hasOwnProperty('text')) + { + let text = ''; + let style = ''; + if (typeof(item['text']) === "string") { + text = hash_get(item, 'text', ''); + style = hash_get(item, 'style', ''); + } else { + let textValues = item['text']; + text = hash_get(textValues, 'text', ''); + style = hash_get(textValues, 'style', ''); + } + + if (text !== '') { + if (!this.widget) { + if (typeof(item['text']) === "string") + this.widget = this._createTextOld(item); + else + this.widget = this._createText(item['text']); + this.insert_child_above(this.widget, this.icon); + } else { + this.widget.label = text; + } + } + + if (style !== '' && this.widget) { + this.widget.set_style(style); } } - if (style !== '' && this.widget) { - this.style = style; - this.widget.set_style(this.style); + if (item.hasOwnProperty('icon')) + { + let icon = ''; + let style = ''; + if (typeof(item['icon']) === "string") { + icon = hash_get(item, 'icon', ''); + style = hash_get(item, 'iconStyle', ''); + } else { + let iconValues = item['icon']; + icon = hash_get(iconValues, 'path', ''); + style = hash_get(textValues, 'style', ''); + } + + if (icon !== '') { + if (typeof(item['icon']) === "string") + this.icon = this._createIconOld(item); + else + this.icon = this._createIcon(item['icon']); + } + + if (prevIcon) { + this.insert_child_above(this.icon, prevIcon); + this.remove_child(prevIcon); + //delete prevIcon; + } else + this.insert_child_before(this.icon, prevWidget); + + if (style !== '' && this.icon) { + this.icon.set_style(style); + } } - if (icon !== '') { - this._createIcon(icon, iconStyle); - if (prevIcon) - this.box.insert_child_above(this.icon, prevIcon); - else - this.box.insert_child_before(this.icon, prevWidget); - } + if (item.hasOwnProperty('popup')) + { + if (this.menuItem) { + this.menu.removeAll(); + //delete this.menuItem; + } - if (iconStyle !== '' && this.icon) { - this.iconStyle = style; - this.icon.set_style(this.iconStyle); - } + this._createPopup(item['popup']); + } - if (prevIcon && icon !== '') - this.box.remove_child(prevIcon); + this.onClick = hash_get(item, 'on-click', this.onClick); + this.onEnter = hash_get(item, 'on-enter', this.onEnter); + this.onLeave = hash_get(item, 'on-leave', this.onLeave); } }); @@ -266,7 +347,7 @@ class GenericMonitorDBUS { constructor() { this.monitor_groups = {}; this.actor_clicked = {}; - this.clutterSettings = clutter.Settings.get_default(); + this.ClutterSettings = Clutter.Settings.get_default(); this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(loadInterfaceXml('dbus.xml'), this); this._dbusImpl.export(Gio.DBus.session, '/com/soutade/GenericMonitor'); this._dbusImpl.emit_signal('onActivate', null); @@ -363,7 +444,7 @@ class GenericMonitorDBUS { clickItem['nbClicks'] = 1; clickItem['button'] = event.get_button(); this.actor_clicked[actorName] = clickItem; - Mainloop.timeout_add(this.clutterSettings['double-click-time'], + Mainloop.timeout_add(this.ClutterSettings['double-click-time'], Lang.bind(this, this._doClickCallback)); } else { this.actor_clicked[actorName]['nbClicks'] = 2; @@ -388,49 +469,29 @@ class GenericMonitorDBUS { for (let itemIndex in parameters['items']) { let item = parameters['items'][itemIndex]; - let style = ''; - if (item.hasOwnProperty('style')) - style = item['style']; - let text = ''; - if (item.hasOwnProperty('text')) - text = item['text']; - let icon = ''; - if (item.hasOwnProperty('icon')) - icon = item['icon']; - let iconStyle = ''; - if (item.hasOwnProperty('icon-style')) - iconStyle = item['icon-style']; - let onClick = ''; - if (item.hasOwnProperty('on-click')) - onClick = item['on-click']; - let box = 'center'; - if (item.hasOwnProperty('box')) - box = item['box']; - let monitorWidget = this._getItemFromGroup(group, item['name']); - let lastWidget = null; // New widget if (monitorWidget === null) { - lastWidget = group.length - 1; + let lastWidget = group.length - 1; monitorWidget = new MonitorWidget(item, groupName, lastWidget); group.push(monitorWidget); // Connect signals - if (onClick !== '') { - if (monitorWidget.widget) - monitorWidget.widget.set_reactive(true); - monitorWidget.widget.connect( - 'button-release-event', Lang.bind(this, this._clicked) - ); - if (monitorWidget.icon) { - monitorWidget.icon.set_reactive(true); - monitorWidget.icon.connect( - 'button-release-event', Lang.bind(this, this._clicked) - ); - } - } + // if (onClick !== '') { + // if (monitorWidget.widget) + // monitorWidget.widget.set_reactive(true); + // monitorWidget.widget.connect( + // 'button-release-event', Lang.bind(this, this._clicked) + // ); + // if (monitorWidget.icon) { + // monitorWidget.icon.set_reactive(true); + // monitorWidget.icon.connect( + // 'button-release-event', Lang.bind(this, this._clicked) + // ); + // } + // } } else { - monitorWidget.update(text, style, icon, iconStyle); + monitorWidget.update(item); } } }