@@ -63,6 +63,7 @@ const PopupMenu = imports.ui.popupMenu;
6363const Settings = imports . ui . settings ;
6464const SignalManager = imports . misc . signalManager ;
6565const Tooltips = imports . ui . tooltips ;
66+ const MessageTray = imports . ui . messageTray ;
6667const WindowUtils = imports . misc . windowUtils ;
6768
6869const MAX_TEXT_LENGTH = 1000 ;
@@ -309,6 +310,27 @@ class AppMenuButton {
309310 this . _label = new St . Label ( ) ;
310311 this . actor . add_actor ( this . _label ) ;
311312
313+ this . notificationsBadge = new St . BoxLayout ( {
314+ style_class : 'grouped-window-list-notifications-badge' ,
315+ important : true ,
316+ x_align : St . Align . MIDDLE ,
317+ y_align : St . Align . MIDDLE ,
318+ show_on_set_parent : false ,
319+ } ) ;
320+ this . notificationsBadgeLabel = new St . Label ( {
321+ style_class : 'grouped-window-list-notifications-badge-label' ,
322+ important : true ,
323+ text : ''
324+ } ) ;
325+ this . notificationsBadgeLabel . clutter_text . ellipsize = false ;
326+ this . notificationsBadge . add ( this . notificationsBadgeLabel , {
327+ x_align : St . Align . START ,
328+ y_align : St . Align . START ,
329+ } ) ;
330+ this . actor . add_child ( this . notificationsBadge ) ;
331+ this . notificationsBadge . set_text_direction ( St . TextDirection . LTR ) ;
332+ this . notificationsBadge . show ( ) ;
333+
312334 this . updateLabelVisible ( ) ;
313335
314336 this . _visible = true ;
@@ -362,6 +384,9 @@ class AppMenuButton {
362384 this . onScrollModeChanged ( ) ;
363385 this . _needsAttention = false ;
364386
387+ this . app = this . _getApp ( ) ;
388+ this . appId = this . app ? this . app . get_id ( ) : null ;
389+ this . updateNotificationsBadge ( ) ;
365390 this . setDisplayTitle ( ) ;
366391 this . onFocus ( ) ;
367392 this . setIcon ( ) ;
@@ -507,10 +532,7 @@ class AppMenuButton {
507532
508533 setDisplayTitle ( ) {
509534 let title = this . metaWindow . get_title ( ) ;
510- let tracker = Cinnamon . WindowTracker . get_default ( ) ;
511- let app = tracker . get_window_app ( this . metaWindow ) ;
512-
513- if ( ! title ) title = app ? app . get_name ( ) : '?' ;
535+ if ( ! title ) title = this . app ? this . app . get_name ( ) : '?' ;
514536
515537 /* Sanitize the window title to prevent dodgy window titles such as
516538 * "); DROP TABLE windows; --. Turn all whitespaces into " " because
@@ -527,6 +549,18 @@ class AppMenuButton {
527549 this . _label . set_text ( title ) ;
528550 }
529551
552+ _getApp ( ) {
553+ const tracker = Cinnamon . WindowTracker . get_default ( ) ;
554+ let app = tracker . get_window_app ( this . metaWindow ) ;
555+ if ( ! app ) {
556+ app = tracker . get_app_from_pid ( this . metaWindow . get_pid ( ) ) ;
557+ }
558+ if ( ! app ) {
559+ app = tracker . get_app_from_pid ( this . metaWindow . get_client_pid ( ) ) ;
560+ }
561+ return app ;
562+ }
563+
530564 destroy ( ) {
531565 if ( this . _flashTimer ) {
532566 Mainloop . source_remove ( this . _flashTimer ) ;
@@ -687,7 +721,8 @@ class AppMenuButton {
687721 let allocWidth = box . x2 - box . x1 ;
688722 let allocHeight = box . y2 - box . y1 ;
689723
690- let childBox = new Clutter . ActorBox ( ) ;
724+ const childBox = new Clutter . ActorBox ( ) ;
725+ const notifBadgeBox = new Clutter . ActorBox ( ) ;
691726
692727 let [ minWidth , minHeight , naturalWidth , naturalHeight ] = this . _iconBox . get_preferred_size ( ) ;
693728
@@ -717,6 +752,22 @@ class AppMenuButton {
717752 }
718753 this . _iconBox . allocate ( childBox , flags ) ;
719754
755+ // Set notifications badge position
756+ const notifBadgeOffset = 3 * global . ui_scale ;
757+ const notifBadgeXCenter = this . _iconBox . x + this . _iconBox . width - notifBadgeOffset ;
758+ const notifBadgeYCenter = this . _iconBox . y + notifBadgeOffset ;
759+ const [ nLabelMinWidth , nLabelMinHeight , nLabelNaturalWidth , nLabelNaturalHeight ] = this . notificationsBadgeLabel . get_preferred_size ( ) ;
760+ const notifBadgesize = Math . max ( nLabelNaturalWidth , nLabelNaturalHeight ) ;
761+ notifBadgeBox . x2 = Math . min ( notifBadgeXCenter + Math . floor ( notifBadgesize / 2 ) , box . x2 ) ;
762+ notifBadgeBox . x1 = notifBadgeBox . x2 - notifBadgesize ;
763+ notifBadgeBox . y1 = Math . max ( notifBadgeYCenter - Math . floor ( notifBadgesize / 2 ) , 0 ) ;
764+ notifBadgeBox . y2 = notifBadgeBox . y1 + notifBadgesize ;
765+ const notifLabelPosX = Math . floor ( ( notifBadgesize - nLabelNaturalWidth ) / 2 ) ;
766+ const notifLabelPosY = Math . floor ( ( notifBadgesize - nLabelNaturalHeight ) / 2 ) ;
767+ this . notificationsBadgeLabel . set_anchor_point ( - notifLabelPosX , - notifLabelPosY ) ;
768+ this . notificationsBadge . set_size ( notifBadgesize , notifBadgesize ) ;
769+ this . notificationsBadge . allocate ( notifBadgeBox , flags ) ;
770+
720771 if ( this . drawLabel ) {
721772 [ minWidth , minHeight , naturalWidth , naturalHeight ] = this . _label . get_preferred_size ( ) ;
722773
@@ -763,13 +814,10 @@ class AppMenuButton {
763814 }
764815
765816 setIcon ( ) {
766- let tracker = Cinnamon . WindowTracker . get_default ( ) ;
767- let app = tracker . get_window_app ( this . metaWindow ) ;
768-
769817 this . icon_size = this . _applet . icon_size ;
770818
771- let icon = app ?
772- app . create_icon_texture_for_window ( this . icon_size , this . metaWindow ) :
819+ let icon = this . app ?
820+ this . app . create_icon_texture_for_window ( this . icon_size , this . metaWindow ) :
773821 new St . Icon ( { icon_name : 'application-default-icon' ,
774822 icon_type : St . IconType . FULLCOLOR ,
775823 icon_size : this . icon_size } ) ;
@@ -815,6 +863,17 @@ class AppMenuButton {
815863 return continueFlashing ;
816864 } ) ;
817865 }
866+
867+ updateNotificationsBadge ( ) {
868+ const nCount = Main . notificationDaemon . getNotificationCountForApp ( this . app ) ;
869+
870+ if ( nCount > 0 && this . _applet . enableNotifications ) {
871+ this . notificationsBadgeLabel . text = nCount . toString ( ) ;
872+ this . notificationsBadge . show ( ) ;
873+ } else {
874+ this . notificationsBadge . hide ( ) ;
875+ }
876+ }
818877} ;
819878
820879class AppMenuButtonRightClickMenu extends Applet . AppletPopupMenu {
@@ -1043,6 +1102,7 @@ class CinnamonWindowListApplet extends Applet.Applet {
10431102 this . settings . bind ( "window-hover" , "windowHover" , this . _onPreviewChanged ) ;
10441103 this . settings . bind ( "window-preview-show-label" , "showLabel" , this . _onPreviewChanged ) ;
10451104 this . settings . bind ( "window-preview-scale" , "previewScale" , this . _onPreviewChanged ) ;
1105+ this . settings . bind ( "enable-notifications" , "enableNotifications" , this . _updateAllNotificationBadges ) ;
10461106 this . settings . bind ( "last-window-order" , "lastWindowOrder" , null ) ;
10471107
10481108 this . signals . connect ( global . display , 'window-created' , this . _onWindowAddedAsync , this ) ;
@@ -1052,6 +1112,7 @@ class CinnamonWindowListApplet extends Applet.Applet {
10521112 this . signals . connect ( Main . panelManager , 'monitors-changed' , this . _updateWatchedMonitors , this ) ;
10531113 this . signals . connect ( global . window_manager , 'switch-workspace' , this . _refreshAllItems , this ) ;
10541114 this . signals . connect ( Cinnamon . WindowTracker . get_default ( ) , "window-app-changed" , this . _onWindowAppChanged , this ) ;
1115+ this . signals . connect ( Main . messageTray , 'notify-applet-update' , this . _onNotificationReceived , this ) ;
10551116
10561117 this . signals . connect ( this . actor , 'style-changed' , Lang . bind ( this , this . _updateSpacing ) ) ;
10571118
@@ -1064,11 +1125,13 @@ class CinnamonWindowListApplet extends Applet.Applet {
10641125 on_applet_added_to_panel ( userEnabled ) {
10651126 this . _updateSpacing ( ) ;
10661127 this . appletEnabled = true ;
1128+ MessageTray . extensionsHandlingNotifications ++ ;
10671129 }
10681130
10691131 on_applet_removed_from_panel ( ) {
10701132 this . signals . disconnectAllSignals ( ) ;
10711133 this . settings . finalize ( ) ;
1134+ MessageTray . extensionsHandlingNotifications -- ;
10721135 }
10731136
10741137 on_applet_instances_changed ( ) {
@@ -1507,6 +1570,40 @@ class CinnamonWindowListApplet extends Applet.Applet {
15071570 this . _tooltipErodeTimer = null ;
15081571 }
15091572 }
1573+
1574+ _onNotificationReceived ( mtray , notification ) {
1575+ let appId = notification . source . app ?. get_id ( ) ;
1576+
1577+ if ( ! appId ) {
1578+ return ;
1579+ }
1580+
1581+ // Add notification to all appMenuButton's with appId
1582+ let notificationAdded = false ;
1583+ this . _windows . forEach ( window => {
1584+ if ( appId === window . appId ) {
1585+ notificationAdded = true ;
1586+ window . updateNotificationsBadge ( ) ;
1587+ }
1588+ } ) ;
1589+
1590+ if ( notificationAdded ) {
1591+ notification . appId = appId ;
1592+ notification . connect ( 'destroy' , ( ) => this . _onNotificationDestroyed ( notification ) ) ;
1593+ }
1594+ }
1595+
1596+ _onNotificationDestroyed ( notification ) {
1597+ this . _windows . forEach ( window => {
1598+ if ( notification . appId === window . appId ) {
1599+ window . updateNotificationsBadge ( ) ;
1600+ }
1601+ } ) ;
1602+ }
1603+
1604+ _updateAllNotificationBadges ( ) {
1605+ this . _windows . forEach ( window => window . updateNotificationsBadge ( ) ) ;
1606+ }
15101607}
15111608
15121609function main ( metadata , orientation , panel_height , instance_id ) {
0 commit comments