notifications: Add header like in the new designs

See:
9e2bed6f37/notifications-calendar/notifications-grouping.png

This also increases the size of the icon to 48px.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/6743
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3103>
This commit is contained in:
Julian Sparber 2023-10-30 16:03:47 +01:00 committed by Marge Bot
parent ff76584937
commit 8fdea10e33
4 changed files with 163 additions and 77 deletions

View File

@ -65,60 +65,96 @@
@extend %card;
padding: 0 !important;
// icon container
.message-icon-bin {
padding: ($base_padding * 3);
// subtract side padding to accommodate the close button
&:ltr { padding-right: $base_padding - 2px; };
&:rtl { padding-left: $base_padding - 2px; };
&:ltr {padding-right:$base_padding;}
&:rtl {padding-left:$base_padding;}
// message header
.message-header {
padding: 0 $scaled_padding;
margin: 0 $base_padding;
margin-top: $base_padding;
color: $insensitive_fg_color;
spacing: $base_padding;
// icon size and color
> StIcon {
icon-size: $large_icon_size;
// header source icon
.message-source-icon {
padding: 2px 0;
icon-size: $scalable_icon_size; // 16px
-st-icon-style: symbolic;
}
// box that contains the source icon, source name and timestamp of the message
.message-header-content {
spacing: $base_padding;
min-height: to_em(24px);
padding-bottom: $base_padding;
// header source title
.message-source-title {
font-weight: bold;
}
// Time label
.event-time {
@extend %caption;
color: $insensitive_fg_color;
// Add bottom padding to align the app name with the time horizontally
padding-bottom: to_em(1px);
&:ltr { text-align: right };
&:rtl { text-align: left };
}
}
// close button
.message-close-button {
@extend .icon-button;
color: $fg_color !important;
background-color: transparentize($fg_color, 0.8);
padding: 0 !important;
border: 4px transparent solid;
&:hover {background-color: transparentize($fg_color, 0.7);}
&:insensitive {background-color: transparentize($fg_color, 0.9);}
&:active {background-color: transparentize($fg_color, 0.8);}
}
}
// content
.message-content {
spacing: 4px;
padding: ($base_padding * 1.5);
margin-bottom: $base_margin * 2;
}
.message-title-box {
// container for message contents
.message-box {
padding: 0 $base_padding;
padding-bottom: $base_padding;
margin: $base_padding;
margin-top: $base_padding;
spacing: $base_padding;
// title
.message-title {
font-weight: bold;
// icon container
.message-icon-bin {
&:ltr {padding-right:$base_padding;}
&:rtl {padding-left:$base_padding;}
// icon size and color
> StIcon {
icon-size: $base_icon_size * 3; // 48px
-st-icon-style: symbolic;
}
}
// notification time stamp
> .event-time {
@extend %caption;
color: $insensitive_fg_color;
padding-bottom: to_em(1px);
// If the header isn't displayed we need more top margin
&:first-child {
margin-top: $base_padding * 2;
}
&:ltr { text-align: right };
&:rtl { text-align: left };
// text of the message
.message-content {
spacing: $base_margin;
// message title
.message-title {
font-weight: bold;
}
}
}
// close button
.message-close-button {
@extend .icon-button;
color: $fg_color;
background-color: transparentize($fg_color, 0.8);
padding: 0 !important;
border: 5px transparent solid;
margin: 1px;
&:hover {background-color: transparentize($fg_color, 0.7);}
&:active {background-color: transparentize($fg_color, 0.8);}
}
// body
.message-body {color: darken($fg_color, 10%);}
}
// URLs in messages
@ -129,7 +165,6 @@
/* Media Controls */
.message-media-control {
padding: 0 $base_padding * 3;
margin: $base_padding * 2 0;
border-radius: $base_border_radius;
color: $fg_color;
border: 1px solid transparent;
@ -158,10 +193,6 @@
}
}
// fix margin for last button
&:last-child:ltr { margin-right: $base_margin * 3; }
&:last-child:rtl { margin-left: $base_margin * 3; }
& StIcon { icon-size: $base_icon_size; }
}

View File

@ -767,7 +767,7 @@ export const Calendar = GObject.registerClass({
export const NotificationMessage = GObject.registerClass(
class NotificationMessage extends MessageList.Message {
_init(notification) {
super._init(notification.title, notification.bannerBodyText);
super._init(notification.source, notification.title, notification.bannerBodyText);
this.setUseBodyMarkup(notification.bannerBodyMarkup);
this.notification = notification;

View File

@ -375,6 +375,67 @@ class TimeLabel extends St.Label {
}
});
const MessageHeader = GObject.registerClass(
class MessageHeader extends St.BoxLayout {
constructor(source) {
super({
style_class: 'message-header',
x_expand: true,
});
const sourceIconEffect = new Clutter.DesaturateEffect();
const sourceIcon = new St.Icon({
style_class: 'message-source-icon',
y_align: Clutter.ActorAlign.CENTER,
fallback_icon_name: 'application-x-executable-symbolic',
});
sourceIcon.add_effect(sourceIconEffect);
this.add_child(sourceIcon);
sourceIcon.connect('style-changed', () => {
const themeNode = sourceIcon.get_theme_node();
sourceIconEffect.enabled = themeNode.get_icon_style() === St.IconStyle.SYMBOLIC;
});
const headerContent = new St.BoxLayout({
style_class: 'message-header-content',
y_align: Clutter.ActorAlign.CENTER,
x_expand: true,
});
this.add_child(headerContent);
this.closeButton = new St.Button({
style_class: 'message-close-button',
icon_name: 'window-close-symbolic',
y_align: Clutter.ActorAlign.CENTER,
opacity: 0,
});
this.add_child(this.closeButton);
const sourceTitle = new St.Label({
style_class: 'message-source-title',
y_align: Clutter.ActorAlign.END,
});
headerContent.add_child(sourceTitle);
source.bind_property_full('title',
sourceTitle,
'text',
GObject.BindingFlags.SYNC_CREATE,
// Translators: this is the string displayed in the header when a message
// source doesn't have a name
(bind, value) => [true, value === null || value === '' ? _('Unknown App') : value],
null);
source.bind_property('icon',
sourceIcon,
'gicon',
GObject.BindingFlags.SYNC_CREATE);
this.timeLabel = new TimeLabel();
headerContent.add_child(this.timeLabel);
}
});
export const Message = GObject.registerClass({
Signals: {
'close': {},
@ -382,7 +443,7 @@ export const Message = GObject.registerClass({
'unexpanded': {},
},
}, class Message extends St.Button {
_init(title, body) {
_init(source, title, body) {
super._init({
style_class: 'message',
accessible_role: Atk.Role.NOTIFICATION,
@ -400,7 +461,12 @@ export const Message = GObject.registerClass({
});
this.set_child(vbox);
let hbox = new St.BoxLayout();
this._header = new MessageHeader(source);
vbox.add_child(this._header);
const hbox = new St.BoxLayout({
style_class: 'message-box',
});
vbox.add_child(hbox);
this._actionBin = new St.Widget({
@ -427,26 +493,12 @@ export const Message = GObject.registerClass({
this._mediaControls = new St.BoxLayout();
hbox.add_child(this._mediaControls);
let titleBox = new St.BoxLayout({style_class: 'message-title-box'});
contentBox.add_child(titleBox);
this.titleLabel = new St.Label({
style_class: 'message-title',
y_align: Clutter.ActorAlign.END,
});
this.setTitle(title);
titleBox.add_child(this.titleLabel);
this._timeLabel = new TimeLabel();
titleBox.add_child(this._timeLabel);
this._closeButton = new St.Button({
style_class: 'message-close-button',
icon_name: 'window-close-symbolic',
y_align: Clutter.ActorAlign.CENTER,
opacity: 0,
});
titleBox.add_child(this._closeButton);
contentBox.add_child(this.titleLabel);
this._bodyStack = new St.Widget({x_expand: true});
this._bodyStack.layout_manager = new LabelExpanderLayout();
@ -457,10 +509,11 @@ export const Message = GObject.registerClass({
this._bodyStack.add_child(this.bodyLabel);
this.setBody(body);
this._closeButton.connect('clicked', this.close.bind(this));
let actorHoverId = this.connect('notify::hover', this._sync.bind(this));
this._closeButton.connect('destroy', this.disconnect.bind(this, actorHoverId));
this.connect('destroy', this._onDestroy.bind(this));
this._header.closeButton.connect('clicked', this.close.bind(this));
let actorHoverId = this.connect('notify::hover', this._sync.bind(this));
this._header.closeButton.connect('destroy', this.disconnect.bind(this, actorHoverId));
this._sync();
}
@ -474,11 +527,11 @@ export const Message = GObject.registerClass({
}
get datetime() {
return this._timeLabel.datetime;
return this._header.timeLabel.datetime;
}
set datetime(datetime) {
this._timeLabel.datetime = datetime;
this._header.timeLabel.datetime = datetime;
}
setTitle(text) {
@ -596,8 +649,8 @@ export const Message = GObject.registerClass({
_sync() {
let visible = this.hover && this.canClose();
this._closeButton.opacity = visible ? 255 : 0;
this._closeButton.reactive = visible;
this._header.closeButton.opacity = visible ? 255 : 0;
this._header.closeButton.reactive = visible;
}
_onDestroy() {

View File

@ -23,17 +23,13 @@ const MPRIS_PLAYER_PREFIX = 'org.mpris.MediaPlayer2.';
export const MediaMessage = GObject.registerClass(
class MediaMessage extends MessageList.Message {
_init(player) {
super._init('', '');
super._init(player.source, '', '');
this._player = player;
this._icon = new St.Icon({style_class: 'media-message-cover-icon'});
this.setIcon(this._icon);
// reclaim space used by unused elements
this._timeLabel.hide();
this._closeButton.hide();
this._prevButton = this.addMediaControl('media-skip-backward-symbolic',
() => {
this._player.previous();
@ -104,6 +100,7 @@ export class MprisPlayer extends Signals.EventEmitter {
this._trackTitle = '';
this._trackCoverUrl = '';
this._busName = busName;
this.source = new MessageList.Source();
}
get status() {
@ -229,6 +226,11 @@ export class MprisPlayer extends Signals.EventEmitter {
this._app = null;
}
this.source.set({
title: this._app?.get_name() ?? null,
icon: this._app?.get_icon() ?? null,
});
this.emit('changed');
let visible = this._playerProxy.CanPlay;