diff --git a/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js index 187f86c0c4..ee4bc210a1 100644 --- a/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/window-list@cinnamon.org/applet.js @@ -63,6 +63,7 @@ const PopupMenu = imports.ui.popupMenu; const Settings = imports.ui.settings; const SignalManager = imports.misc.signalManager; const Tooltips = imports.ui.tooltips; +const MessageTray = imports.ui.messageTray; const WindowUtils = imports.misc.windowUtils; const MAX_TEXT_LENGTH = 1000; @@ -309,6 +310,27 @@ class AppMenuButton { this._label = new St.Label(); this.actor.add_actor(this._label); + this.notificationsBadge = new St.BoxLayout({ + style_class: 'grouped-window-list-notifications-badge', + important: true, + x_align: St.Align.MIDDLE, + y_align: St.Align.MIDDLE, + show_on_set_parent: false, + }); + this.notificationsBadgeLabel = new St.Label({ + style_class: 'grouped-window-list-notifications-badge-label', + important: true, + text: '' + }); + this.notificationsBadgeLabel.clutter_text.ellipsize = false; + this.notificationsBadge.add(this.notificationsBadgeLabel, { + x_align: St.Align.START, + y_align: St.Align.START, + }); + this.actor.add_child(this.notificationsBadge); + this.notificationsBadge.set_text_direction(St.TextDirection.LTR); + this.notificationsBadge.show(); + this.updateLabelVisible(); this._visible = true; @@ -362,6 +384,9 @@ class AppMenuButton { this.onScrollModeChanged(); this._needsAttention = false; + this.app = this._getApp(); + this.appId = this.app ? this.app.get_id() : null; + this.updateNotificationsBadge(); this.setDisplayTitle(); this.onFocus(); this.setIcon(); @@ -507,10 +532,7 @@ class AppMenuButton { setDisplayTitle() { let title = this.metaWindow.get_title(); - let tracker = Cinnamon.WindowTracker.get_default(); - let app = tracker.get_window_app(this.metaWindow); - - if (!title) title = app ? app.get_name() : '?'; + if (!title) title = this.app ? this.app.get_name() : '?'; /* Sanitize the window title to prevent dodgy window titles such as * "); DROP TABLE windows; --. Turn all whitespaces into " " because @@ -527,6 +549,18 @@ class AppMenuButton { this._label.set_text(title); } + _getApp() { + const tracker = Cinnamon.WindowTracker.get_default(); + let app = tracker.get_window_app(this.metaWindow); + if (!app) { + app = tracker.get_app_from_pid(this.metaWindow.get_pid()); + } + if (!app) { + app = tracker.get_app_from_pid(this.metaWindow.get_client_pid()); + } + return app; + } + destroy() { if (this._flashTimer) { Mainloop.source_remove(this._flashTimer); @@ -687,7 +721,8 @@ class AppMenuButton { let allocWidth = box.x2 - box.x1; let allocHeight = box.y2 - box.y1; - let childBox = new Clutter.ActorBox(); + const childBox = new Clutter.ActorBox(); + const notifBadgeBox = new Clutter.ActorBox(); let [minWidth, minHeight, naturalWidth, naturalHeight] = this._iconBox.get_preferred_size(); @@ -717,6 +752,22 @@ class AppMenuButton { } this._iconBox.allocate(childBox, flags); + // Set notifications badge position + const notifBadgeOffset = 3 * global.ui_scale; + const notifBadgeXCenter = this._iconBox.x + this._iconBox.width - notifBadgeOffset; + const notifBadgeYCenter = this._iconBox.y + notifBadgeOffset; + const [nLabelMinWidth, nLabelMinHeight, nLabelNaturalWidth, nLabelNaturalHeight] = this.notificationsBadgeLabel.get_preferred_size(); + const notifBadgesize = Math.max(nLabelNaturalWidth, nLabelNaturalHeight); + notifBadgeBox.x2 = Math.min(notifBadgeXCenter + Math.floor(notifBadgesize / 2), box.x2); + notifBadgeBox.x1 = notifBadgeBox.x2 - notifBadgesize; + notifBadgeBox.y1 = Math.max(notifBadgeYCenter - Math.floor(notifBadgesize / 2), 0); + notifBadgeBox.y2 = notifBadgeBox.y1 + notifBadgesize; + const notifLabelPosX = Math.floor((notifBadgesize - nLabelNaturalWidth) / 2); + const notifLabelPosY = Math.floor((notifBadgesize - nLabelNaturalHeight) / 2); + this.notificationsBadgeLabel.set_anchor_point(-notifLabelPosX, -notifLabelPosY); + this.notificationsBadge.set_size(notifBadgesize, notifBadgesize); + this.notificationsBadge.allocate(notifBadgeBox, flags); + if (this.drawLabel) { [minWidth, minHeight, naturalWidth, naturalHeight] = this._label.get_preferred_size(); @@ -763,13 +814,10 @@ class AppMenuButton { } setIcon() { - let tracker = Cinnamon.WindowTracker.get_default(); - let app = tracker.get_window_app(this.metaWindow); - this.icon_size = this._applet.icon_size; - let icon = app ? - app.create_icon_texture_for_window(this.icon_size, this.metaWindow) : + let icon = this.app ? + this.app.create_icon_texture_for_window(this.icon_size, this.metaWindow) : new St.Icon({ icon_name: 'application-default-icon', icon_type: St.IconType.FULLCOLOR, icon_size: this.icon_size }); @@ -815,6 +863,17 @@ class AppMenuButton { return continueFlashing; }); } + + updateNotificationsBadge() { + const nCount = Main.notificationDaemon.getNotificationCountForApp(this.app); + + if (nCount > 0 && this._applet.enableNotifications) { + this.notificationsBadgeLabel.text = nCount.toString(); + this.notificationsBadge.show(); + } else { + this.notificationsBadge.hide(); + } + } }; class AppMenuButtonRightClickMenu extends Applet.AppletPopupMenu { @@ -1043,6 +1102,7 @@ class CinnamonWindowListApplet extends Applet.Applet { this.settings.bind("window-hover", "windowHover", this._onPreviewChanged); this.settings.bind("window-preview-show-label", "showLabel", this._onPreviewChanged); this.settings.bind("window-preview-scale", "previewScale", this._onPreviewChanged); + this.settings.bind("enable-notifications", "enableNotifications", this._updateAllNotificationBadges); this.settings.bind("last-window-order", "lastWindowOrder", null); this.signals.connect(global.display, 'window-created', this._onWindowAddedAsync, this); @@ -1052,6 +1112,7 @@ class CinnamonWindowListApplet extends Applet.Applet { this.signals.connect(Main.panelManager, 'monitors-changed', this._updateWatchedMonitors, this); this.signals.connect(global.window_manager, 'switch-workspace', this._refreshAllItems, this); this.signals.connect(Cinnamon.WindowTracker.get_default(), "window-app-changed", this._onWindowAppChanged, this); + this.signals.connect(Main.messageTray, 'notify-applet-update', this._onNotificationReceived, this); this.signals.connect(this.actor, 'style-changed', Lang.bind(this, this._updateSpacing)); @@ -1064,11 +1125,13 @@ class CinnamonWindowListApplet extends Applet.Applet { on_applet_added_to_panel(userEnabled) { this._updateSpacing(); this.appletEnabled = true; + MessageTray.extensionsHandlingNotifications++; } on_applet_removed_from_panel() { this.signals.disconnectAllSignals(); this.settings.finalize(); + MessageTray.extensionsHandlingNotifications--; } on_applet_instances_changed() { @@ -1507,6 +1570,40 @@ class CinnamonWindowListApplet extends Applet.Applet { this._tooltipErodeTimer = null; } } + + _onNotificationReceived(mtray, notification) { + let appId = notification.source.app?.get_id(); + + if (!appId) { + return; + } + + // Add notification to all appMenuButton's with appId + let notificationAdded = false; + this._windows.forEach(window => { + if (appId === window.appId) { + notificationAdded = true; + window.updateNotificationsBadge(); + } + }); + + if (notificationAdded) { + notification.appId = appId; + notification.connect('destroy', () => this._onNotificationDestroyed(notification)); + } + } + + _onNotificationDestroyed(notification) { + this._windows.forEach(window => { + if (notification.appId === window.appId) { + window.updateNotificationsBadge(); + } + }); + } + + _updateAllNotificationBadges() { + this._windows.forEach(window => window.updateNotificationsBadge()); + } } function main(metadata, orientation, panel_height, instance_id) { diff --git a/files/usr/share/cinnamon/applets/window-list@cinnamon.org/settings-schema.json b/files/usr/share/cinnamon/applets/window-list@cinnamon.org/settings-schema.json index 04b4cf4090..aa72e0d004 100644 --- a/files/usr/share/cinnamon/applets/window-list@cinnamon.org/settings-schema.json +++ b/files/usr/share/cinnamon/applets/window-list@cinnamon.org/settings-schema.json @@ -87,6 +87,11 @@ }, "dependency": "window-hover=thumbnail" }, + "enable-notifications": { + "type": "switch", + "default": true, + "description": "Show notification badges" + }, "last-window-order": { "type": "generic", "default": ""