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
2 changes: 2 additions & 0 deletions data/theme/cinnamon-sass/_colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ $destructive_color: #ff7b63;
$warning_color: #f8e45c;
$warning_bg_color: #cd9309;

$notification_badge_bg_color: #e74b37;

$accent_color: #78aeed;
$accent_bg_color: #3584e4;

Expand Down
13 changes: 12 additions & 1 deletion data/theme/cinnamon-sass/widgets/_windowlist.scss
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,25 @@
&-button-label { padding-left: 4px;}

&-number-label {
font-size: 0.8em;
font-size: 10px;
z-index: 99;
}

&-badge {
border-radius: 9999px;
background-color: $bg_color;
}

&-notifications-badge {
border-radius: 9999px;
background-color: $notification_badge_bg_color;
color: $fg_color;
font-size: 13px;
}

&-notifications-badge-label {
z-index: 99;
}
}

// classic window list
Expand Down
112 changes: 80 additions & 32 deletions files/usr/share/cinnamon/applets/[email protected]/appGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,31 +119,50 @@ class AppGroup {
});
this.actor.add_child(this.progressOverlay);

// Create the app button icon, number label, and text label for titleDisplay
// Create the app button icon, window count and notification badges, and text label for titleDisplay
this.iconBox = new Cinnamon.Slicer({name: 'appMenuIcon'});
this.actor.add_child(this.iconBox);
this.setActorAttributes(null, params.metaWindow);

this.badge = new St.BoxLayout({
this.windowsBadge = new St.BoxLayout({
style_class: 'grouped-window-list-badge',
important: true,
x_align: St.Align.START,
x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE,
show_on_set_parent: false,
});
this.numberLabel = new St.Label({
this.windowsBadgeLabel = new St.Label({
style_class: 'grouped-window-list-number-label',
important: true,
text: '',
anchor_x: -3 * global.ui_scale,
text: ''
});
this.windowsBadgeLabel.clutter_text.ellipsize = false;
this.windowsBadge.add(this.windowsBadgeLabel, {
x_align: St.Align.START,
y_align: St.Align.START,
});
this.actor.add_child(this.windowsBadge);
this.windowsBadge.set_text_direction(St.TextDirection.LTR);

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.numberLabel.clutter_text.ellipsize = false;
this.badge.add(this.numberLabel, {
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.badge);
this.badge.set_text_direction(St.TextDirection.LTR);
this.actor.add_child(this.notificationsBadge);
this.notificationsBadge.set_text_direction(St.TextDirection.LTR);

this.label = new St.Label({
style_class: 'grouped-window-list-button-label',
Expand Down Expand Up @@ -372,6 +391,8 @@ class AppGroup {
const allocWidth = box.x2 - box.x1;
const allocHeight = box.y2 - box.y1;
const childBox = new Clutter.ActorBox();
const windowBadgeBox = new Clutter.ActorBox();
const notifBadgeBox = new Clutter.ActorBox();
const direction = this.actor.get_text_direction();

// Set the icon to be left-justified (or right-justified) and centered vertically
Expand All @@ -394,15 +415,37 @@ class AppGroup {

this.iconBox.allocate(childBox, flags);

// Set badge position
const windowCountFactor = this.groupState.windowCount > 9 ? 1.5 : 2;
const badgeOffset = 2 * global.ui_scale;
childBox.x1 = childBox.x1 - badgeOffset;
childBox.x2 = childBox.x1 + (this.numberLabel.width * windowCountFactor);
childBox.y1 = Math.max(childBox.y1 - badgeOffset, 0);
childBox.y2 = childBox.y1 + this.badge.get_preferred_height(childBox.get_width())[1];

this.badge.allocate(childBox, flags);
// Set windows badge position
const windowBadgeOffset = 3 * global.ui_scale;
const windowBadgeXCenter = this.iconBox.x + windowBadgeOffset;
const windowBadgeYCenter = this.iconBox.y + windowBadgeOffset;
const [wLabelMinWidth, wLabelMinHeight, wLabelNaturalWidth, wLabelNaturalHeight] = this.windowsBadgeLabel.get_preferred_size();
const windowBadgesize = Math.max(wLabelNaturalWidth, wLabelNaturalHeight);
windowBadgeBox.x1 = Math.max(windowBadgeXCenter - Math.floor(windowBadgesize / 2), 0);
windowBadgeBox.x2 = windowBadgeBox.x1 + windowBadgesize;
windowBadgeBox.y1 = Math.max(windowBadgeYCenter - Math.floor(windowBadgesize / 2), 0);
windowBadgeBox.y2 = windowBadgeBox.y1 + windowBadgesize;
const windowLabelPosX = Math.floor((windowBadgesize - wLabelNaturalWidth) / 2);
const windowLabelPosY = Math.floor((windowBadgesize - wLabelNaturalHeight) / 2);
this.windowsBadgeLabel.set_anchor_point(-windowLabelPosX, -windowLabelPosY);
this.windowsBadge.set_size(windowBadgesize, windowBadgesize);
this.windowsBadge.allocate(windowBadgeBox, 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);

// Set label position
if (this.drawLabel) {
Expand Down Expand Up @@ -676,8 +719,8 @@ class AppGroup {
}

showOrderLabel(number) {
this.numberLabel.text = (number + 1).toString();
this.badge.show();
this.windowsBadgeLabel.text = (number + 1).toString();
this.windowsBadge.show();
}

launchNewInstance(offload=false) {
Expand Down Expand Up @@ -917,6 +960,7 @@ class AppGroup {
this.setIcon(metaWindow)

this.calcWindowNumber();
this.updateNotificationsBadge();
this.onFocusChange();
}
set({
Expand Down Expand Up @@ -1074,20 +1118,24 @@ class AppGroup {
calcWindowNumber() {
if (this.groupState.willUnmount) return;

const windowCount = this.groupState.metaWindows ? this.groupState.metaWindows.length : 0;
this.numberLabel.text = windowCount.toString();

this.groupState.set({windowCount});
this.groupState.set({windowCount: this.groupState.metaWindows ? this.groupState.metaWindows.length : 0});

if (this.groupState.windowCount > 1 && this.state.settings.enableWindowCountBadges) {
this.windowsBadgeLabel.text = this.groupState.windowCount.toString();
this.windowsBadge.show();
} else {
this.windowsBadge.hide();
}
}

if (this.state.settings.numDisplay) {
if (windowCount <= 1) {
this.badge.hide();
} else {
this.badge.show();
updateNotificationsBadge() {
const nCount = Main.notificationDaemon.getNotificationCountForApp(this.groupState.app);

}
if (nCount > 0 && this.state.settings.enableNotificationBadges) {
this.notificationsBadgeLabel.text = nCount.toString();
this.notificationsBadge.show();
} else {
this.badge.hide();
this.notificationsBadge.hide();
}
}

Expand Down
62 changes: 59 additions & 3 deletions files/usr/share/cinnamon/applets/[email protected]/applet.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const Applet = imports.ui.applet;
const Cinnamon = imports.gi.Cinnamon;
const Main = imports.ui.main;
const DND = imports.ui.dnd;
const MessageTray = imports.ui.messageTray;
const {AppletSettings} = imports.ui.settings;
const {SignalManager} = imports.misc.signalManager;
const {throttle, unref, trySpawnCommandLine} = imports.misc.util;
Expand Down Expand Up @@ -254,7 +255,7 @@ class GroupedWindowListApplet extends Applet.Applet {
cycleWindows: (e, source) => this.handleScroll(e, source),
openAbout: () => this.openAbout(),
configureApplet: () => this.configureApplet(),
removeApplet: (event) => this.confirmRemoveApplet(event),
removeApplet: (event) => this.confirmRemoveApplet(event)
});

this.settings = new AppletSettings(this.state.settings, metadata.uuid, instance_id);
Expand Down Expand Up @@ -291,6 +292,7 @@ class GroupedWindowListApplet extends Applet.Applet {
this.signals.connect(global.display, 'window-created', (...args) => this.onWindowCreated(...args));
this.signals.connect(global.settings, 'changed::panel-edit-mode', (...args) => this.on_panel_edit_mode_changed(...args));
this.signals.connect(Main.themeManager, 'theme-set', (...args) => this.refreshCurrentWorkspace(...args));
this.signals.connect(Main.messageTray, 'notify-applet-update', this._onNotificationReceived.bind(this));
}

bindSettings() {
Expand All @@ -307,7 +309,8 @@ class GroupedWindowListApplet extends Applet.Applet {
{key: 'super-num-hotkeys', value: 'SuperNumHotkeys', cb: this.bindAppKeys},
{key: 'title-display', value: 'titleDisplay', cb: this.updateTitleDisplay},
{key: 'launcher-animation-effect', value: 'launcherAnimationEffect', cb: null},
{key: 'number-display', value: 'numDisplay', cb: this.updateWindowNumberState},
{key: 'enable-window-count-badges', value: 'enableWindowCountBadges', cb: this.onEnableWindowCountBadgeChange},
{key: 'enable-notification-badges', value: 'enableNotificationBadges', cb: this.onEnableNotificationsChange},
{key: 'enable-app-button-dragging', value: 'enableDragging', cb: this.draggableSettingChanged},
{key: 'thumbnail-scroll-behavior', value: 'thumbnailScrollBehavior', cb: null},
{key: 'show-thumbnails', value: 'showThumbs', cb: this.updateVerticalThumbnailState},
Expand Down Expand Up @@ -357,6 +360,7 @@ class GroupedWindowListApplet extends Applet.Applet {
}
this.bindAppKeys();
this.state.set({appletReady: true});
MessageTray.extensionsHandlingNotifications++;
}

_updateState(initialUpdate) {
Expand Down Expand Up @@ -424,6 +428,7 @@ class GroupedWindowListApplet extends Applet.Applet {
});
this.settings.finalize();
unref(this, RESERVE_KEYS);
MessageTray.extensionsHandlingNotifications--;
}

on_panel_icon_size_changed(iconSize) {
Expand Down Expand Up @@ -584,7 +589,7 @@ class GroupedWindowListApplet extends Applet.Applet {
});
}

updateWindowNumberState() {
onEnableWindowCountBadgeChange() {
this.workspaces.forEach(
workspace => workspace.calcAllWindowNumbers()
);
Expand Down Expand Up @@ -1022,6 +1027,57 @@ class GroupedWindowListApplet extends Applet.Applet {
this.state.set({thumbnailCloseButtonOffset: global.ui_scale > 1 ? -10 : 0});
this.refreshAllWorkspaces();
}

_onNotificationReceived(mtray, notification) {
let appId = notification.source.app?.get_id();

if (!appId) {
return;
}

// Add notification to all appgroups with appId.
let notificationAdded = false;

this.workspaces.forEach(workspace => {
if (!workspace) return;
workspace.appGroups.forEach(appGroup => {
if (!appGroup || !appGroup.groupState || appGroup.groupState.willUnmount) return;
if (appId === appGroup.groupState.appId) {
notificationAdded = true;
appGroup.updateNotificationsBadge();
}
});
});

if (notificationAdded) {
notification.appId = appId;
notification.connect('destroy', () => this._onNotificationDestroyed(notification));
}
}

_onNotificationDestroyed(notification) {
if (!this.workspaces) return;

this.workspaces.forEach(workspace => {
if (!workspace) return;
workspace.appGroups.forEach(appGroup => {
if (!appGroup || !appGroup.groupState || appGroup.groupState.willUnmount) return;
if (notification.appId === appGroup.groupState.appId) {
appGroup.updateNotificationsBadge();
}
});
});
}

onEnableNotificationsChange() {
this.workspaces.forEach(workspace => {
if (!workspace) return;
workspace.appGroups.forEach(appGroup => {
if (!appGroup || !appGroup.groupState || appGroup.groupState.willUnmount) return;
appGroup.updateNotificationsBadge();
});
});
}
}

function main(metadata, orientation, panel_height, instance_id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"keys": [
"title-display",
"launcher-animation-effect",
"number-display",
"enable-window-count-badges",
"enable-notification-badges",
"enable-app-button-dragging"
]
},
Expand Down Expand Up @@ -184,10 +185,17 @@
"Scale": 3
}
},
"number-display": {
"enable-window-count-badges": {
"type": "checkbox",
"default": true,
"description": "Show window count numbers"
"description": "Show window count badges",
"tooltip": "Indicate on the panel the number of open windows an application has"
},
"enable-notification-badges": {
"type": "checkbox",
"default": true,
"description": "Show notification badges",
"tooltip": "Indicate on the panel when an application has notifications"
},
"enable-app-button-dragging": {
"type": "checkbox",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ class Workspace {

calcAllWindowNumbers() {
this.appGroups.forEach( appGroup => {
appGroup.calcWindowNumber(appGroup.groupState.metaWindows);
appGroup.calcWindowNumber();
});
}

Expand Down
7 changes: 7 additions & 0 deletions files/usr/share/cinnamon/applets/[email protected]/applet.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const PopupMenu = imports.ui.popupMenu;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const Urgency = imports.ui.messageTray.Urgency;
const MessageTray = imports.ui.messageTray;
const NotificationDestroyedReason = imports.ui.messageTray.NotificationDestroyedReason;
const Settings = imports.ui.settings;
const Gettext = imports.gettext.domain("cinnamon-applets");
Expand Down Expand Up @@ -55,6 +56,11 @@ class CinnamonNotificationsApplet extends Applet.TextIconApplet {
Main.keybindingManager.removeXletHotKey(this, "notification-open");
Main.keybindingManager.removeXletHotKey(this, "notification-clear");
global.settings.disconnect(this.panelEditModeHandler);

MessageTray.extensionsHandlingNotifications--;
if (MessageTray.extensionsHandlingNotifications === 0) {
this._clear_all();
}
}

_openMenu() {
Expand Down Expand Up @@ -266,6 +272,7 @@ class CinnamonNotificationsApplet extends Applet.TextIconApplet {

on_applet_added_to_panel() {
this.on_orientation_changed(this._orientation);
MessageTray.extensionsHandlingNotifications++;
}

on_orientation_changed (orientation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
"uuid": "[email protected]",
"name": "Notifications",
"description": "Click to display and manage system notifications",
"role": "notifications",
"icon": "cs-notifications"
}
Loading
Loading