Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 139 additions & 10 deletions files/usr/share/cinnamon/applets/[email protected]/applet.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -362,6 +384,11 @@ class AppMenuButton {
this.onScrollModeChanged();
this._needsAttention = false;

this.app = this._getApp();
this.appId = this.app ? this.app.get_id() : null;
this.notifications = [];
this._applet.copyNotifications(this);
this.updateNotificationsBadge();
this.setDisplayTitle();
this.onFocus();
this.setIcon();
Expand Down Expand Up @@ -507,10 +534,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
Expand All @@ -527,6 +551,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);
Expand Down Expand Up @@ -687,7 +723,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();

Expand Down Expand Up @@ -717,6 +754,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 notifBadgesize = Math.max(this.notificationsBadgeLabel.width, this.notificationsBadgeLabel.height);
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 [nLabelMinWidth, nLabelMinHeight, nLabelNaturalWidth, nLabelNaturalHeight] = this.notificationsBadgeLabel.get_preferred_size();
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();

Expand Down Expand Up @@ -763,13 +816,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 });
Expand Down Expand Up @@ -815,6 +865,15 @@ class AppMenuButton {
return continueFlashing;
});
}

updateNotificationsBadge() {
if (this.notifications.length > 0 && this._applet.enableNotifications) {
this.notificationsBadgeLabel.text = this.notifications.length.toString();
this.notificationsBadge.show();
} else {
this.notificationsBadge.hide();
}
}
};

class AppMenuButtonRightClickMenu extends Applet.AppletPopupMenu {
Expand Down Expand Up @@ -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);
Expand All @@ -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));

Expand All @@ -1064,11 +1125,16 @@ 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--;
if (MessageTray.extensionsHandlingNotifications === 0) {
this._destroyAllNotifications();
}
}

on_applet_instances_changed() {
Expand Down Expand Up @@ -1507,6 +1573,69 @@ 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) {
window.notifications.push(notification);
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) {
const index = window.notifications.indexOf(notification);
if (index > -1) {
window.notifications.splice(index, 1)
window.updateNotificationsBadge();
}
}
});
}

copyNotifications(newAppWindow) {
if (!newAppWindow.appId) return;

// Copy notifications from any existing appWindow with the same appId.
this._windows.some(window => {
if (window.appId === newAppWindow.appId) {
newAppWindow.notifications = window.notifications.slice(); // Shallow copy.
return true;
}
return false;
});
}

_updateAllNotificationBadges() {
this._windows.forEach(window => window.updateNotificationsBadge());
}

_destroyAllNotifications() {
this._windows.forEach(window => {
let i;
// Iterate backwards due to in place element deletion.
for (i = window.notifications.length - 1; i >= 0; i--) {
window.notifications[i].destroy();
}

});
}
}

function main(metadata, orientation, panel_height, instance_id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@
},
"dependency": "window-hover=thumbnail"
},
"enable-notifications": {
"type": "switch",
"default": true,
"description": "Show notification badges"
},
"last-window-order": {
"type": "generic",
"default": ""
Expand Down
Loading