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:
parent
ff76584937
commit
8fdea10e33
@ -65,38 +65,41 @@
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
// content
|
||||
.message-content {
|
||||
spacing: 4px;
|
||||
padding: ($base_padding * 1.5);
|
||||
margin-bottom: $base_margin * 2;
|
||||
}
|
||||
|
||||
.message-title-box {
|
||||
// box that contains the source icon, source name and timestamp of the message
|
||||
.message-header-content {
|
||||
spacing: $base_padding;
|
||||
// title
|
||||
.message-title {
|
||||
min-height: to_em(24px);
|
||||
padding-bottom: $base_padding;
|
||||
|
||||
// header source title
|
||||
.message-source-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
// notification time stamp
|
||||
> .event-time {
|
||||
// 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 };
|
||||
@ -104,21 +107,54 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// close button
|
||||
.message-close-button {
|
||||
@extend .icon-button;
|
||||
color: $fg_color;
|
||||
color: $fg_color !important;
|
||||
background-color: transparentize($fg_color, 0.8);
|
||||
padding: 0 !important;
|
||||
border: 5px transparent solid;
|
||||
margin: 1px;
|
||||
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);}
|
||||
}
|
||||
}
|
||||
|
||||
// body
|
||||
.message-body {color: darken($fg_color, 10%);}
|
||||
// container for message contents
|
||||
.message-box {
|
||||
padding: 0 $base_padding;
|
||||
padding-bottom: $base_padding;
|
||||
margin: $base_padding;
|
||||
margin-top: $base_padding;
|
||||
spacing: $base_padding;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// If the header isn't displayed we need more top margin
|
||||
&:first-child {
|
||||
margin-top: $base_padding * 2;
|
||||
}
|
||||
|
||||
// text of the message
|
||||
.message-content {
|
||||
spacing: $base_margin;
|
||||
|
||||
// message title
|
||||
.message-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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; }
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user