messageList: Add notification group
This add a widget for the new notification grouping
according to the new designs [1], but the expand behavior will be added
in a future commit.
[1] 9e2bed6f37/notifications-calendar/notifications-grouping.png
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3012>
This commit is contained in:
parent
0ac129bbd6
commit
219cd88c82
@ -58,6 +58,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// message notification group
|
||||||
|
.message-notification-group {
|
||||||
|
spacing: $base_padding * 2;
|
||||||
|
|
||||||
|
.message-group-header {
|
||||||
|
padding: $base_padding;
|
||||||
|
.message-group-title {
|
||||||
|
@extend %title_2;
|
||||||
|
margin: 0 $base_margin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// message bubbles
|
// message bubbles
|
||||||
.message {
|
.message {
|
||||||
@extend %card;
|
@extend %card;
|
||||||
|
@ -822,6 +822,196 @@ class MediaMessage extends Message {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const NotificationMessageGroup = GObject.registerClass({
|
||||||
|
Properties: {
|
||||||
|
'has-urgent': GObject.ParamSpec.boolean(
|
||||||
|
'has-urgent', null, null,
|
||||||
|
GObject.ParamFlags.READWRITE,
|
||||||
|
false),
|
||||||
|
'focus-child': GObject.ParamSpec.object(
|
||||||
|
'focus-child', null, null,
|
||||||
|
GObject.ParamFlags.READABLE,
|
||||||
|
Message),
|
||||||
|
},
|
||||||
|
Signals: {
|
||||||
|
'notification-added': {},
|
||||||
|
},
|
||||||
|
}, class NotificationMessageGroup extends St.Widget {
|
||||||
|
constructor(source) {
|
||||||
|
const header = new St.BoxLayout({
|
||||||
|
style_class: 'message-group-header',
|
||||||
|
x_expand: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
super({
|
||||||
|
style_class: 'message-notification-group',
|
||||||
|
x_expand: true,
|
||||||
|
layout_manager: new Clutter.BoxLayout({
|
||||||
|
orientation: Clutter.Orientation.VERTICAL,
|
||||||
|
}),
|
||||||
|
reactive: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._headerBox = header;
|
||||||
|
|
||||||
|
this.source = source;
|
||||||
|
this._notificationToMessage = new Map();
|
||||||
|
this._nUrgent = 0;
|
||||||
|
this._focusChild = null;
|
||||||
|
|
||||||
|
const titleLabel = new St.Label({
|
||||||
|
style_class: 'message-group-title',
|
||||||
|
y_align: Clutter.ActorAlign.CENTER,
|
||||||
|
});
|
||||||
|
|
||||||
|
source.bind_property('title',
|
||||||
|
titleLabel,
|
||||||
|
'text',
|
||||||
|
GObject.BindingFlags.SYNC_CREATE);
|
||||||
|
|
||||||
|
this._headerBox.add_child(titleLabel);
|
||||||
|
|
||||||
|
this.add_child(this._headerBox);
|
||||||
|
|
||||||
|
source.connectObject(
|
||||||
|
'notification-added', (_, notification) => this._addNotification(notification),
|
||||||
|
'notification-removed', (_, notification) => this._removeNotification(notification),
|
||||||
|
this);
|
||||||
|
|
||||||
|
source.notifications.forEach(notification => {
|
||||||
|
this._addNotification(notification);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasUrgent() {
|
||||||
|
return this._nUrgent > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onKeyFocusIn(actor) {
|
||||||
|
if (this._focusChild === actor)
|
||||||
|
return;
|
||||||
|
this._focusChild = actor;
|
||||||
|
this.notify('focus-child');
|
||||||
|
}
|
||||||
|
|
||||||
|
get focusChild() {
|
||||||
|
return this._focusChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
canClose() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_addNotification(notification) {
|
||||||
|
const message = new NotificationMessage(notification);
|
||||||
|
|
||||||
|
this._notificationToMessage.set(notification, message);
|
||||||
|
|
||||||
|
notification.connectObject(
|
||||||
|
'notify::urgency', () => {
|
||||||
|
const isUrgent = notification.urgency === MessageTray.Urgency.CRITICAL;
|
||||||
|
const oldHasUrgent = this.hasUrgent;
|
||||||
|
|
||||||
|
if (isUrgent)
|
||||||
|
this._nUrgent++;
|
||||||
|
else
|
||||||
|
this._nUrgent--;
|
||||||
|
|
||||||
|
const index = isUrgent ? 0 : this._nUrgent;
|
||||||
|
this._moveMessage(message, index);
|
||||||
|
if (oldHasUrgent !== this.hasUrgent)
|
||||||
|
this.notify('has-urgent');
|
||||||
|
}, message);
|
||||||
|
|
||||||
|
const isUrgent = notification.urgency === MessageTray.Urgency.CRITICAL;
|
||||||
|
const oldHasUrgent = this.hasUrgent;
|
||||||
|
|
||||||
|
if (isUrgent)
|
||||||
|
this._nUrgent++;
|
||||||
|
|
||||||
|
const item = new St.Bin({
|
||||||
|
child: message,
|
||||||
|
canFocus: false,
|
||||||
|
layout_manager: new ScaleLayout(),
|
||||||
|
pivot_point: new Graphene.Point({x: .5, y: .5}),
|
||||||
|
scale_x: 0,
|
||||||
|
scale_y: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
message.connectObject('key-focus-in', this._onKeyFocusIn.bind(this), this);
|
||||||
|
|
||||||
|
let index = isUrgent ? 0 : this._nUrgent;
|
||||||
|
this.insert_child_at_index(item, index);
|
||||||
|
|
||||||
|
// The first message doesn't need to be animated since the entire group is animated
|
||||||
|
if (this._notificationToMessage.size > 1) {
|
||||||
|
item.ease({
|
||||||
|
scale_x: 1,
|
||||||
|
scale_y: 1,
|
||||||
|
duration: MESSAGE_ANIMATION_TIME,
|
||||||
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
item.set_scale(1.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldHasUrgent !== this.hasUrgent)
|
||||||
|
this.notify('has-urgent');
|
||||||
|
this.emit('notification-added');
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeNotification(notification) {
|
||||||
|
const message = this._notificationToMessage.get(notification);
|
||||||
|
const item = message.get_parent();
|
||||||
|
|
||||||
|
if (notification.urgency === MessageTray.Urgency.CRITICAL)
|
||||||
|
this._nUrgent--;
|
||||||
|
|
||||||
|
message.disconnectObject(this);
|
||||||
|
|
||||||
|
item.ease({
|
||||||
|
scale_x: 0,
|
||||||
|
scale_y: 0,
|
||||||
|
duration: MESSAGE_ANIMATION_TIME,
|
||||||
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||||
|
onComplete: () => {
|
||||||
|
item.destroy();
|
||||||
|
this._notificationToMessage.delete(notification);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
vfunc_map() {
|
||||||
|
// Acknowledge all notifications once they are mapped
|
||||||
|
this._notificationToMessage.forEach((_, notification) => {
|
||||||
|
notification.acknowledged = true;
|
||||||
|
});
|
||||||
|
super.vfunc_map();
|
||||||
|
}
|
||||||
|
|
||||||
|
_moveMessage(message, index) {
|
||||||
|
if (this.get_child_at_index(index) === message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const item = message.get_parent();
|
||||||
|
item.ease({
|
||||||
|
scale_x: 0,
|
||||||
|
scale_y: 0,
|
||||||
|
duration: MESSAGE_ANIMATION_TIME,
|
||||||
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||||
|
onComplete: () => {
|
||||||
|
this.set_child_at_index(item, index);
|
||||||
|
item.ease({
|
||||||
|
scale_x: 1,
|
||||||
|
scale_y: 1,
|
||||||
|
duration: MESSAGE_ANIMATION_TIME,
|
||||||
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export const MessageView = GObject.registerClass({
|
export const MessageView = GObject.registerClass({
|
||||||
Properties: {
|
Properties: {
|
||||||
'can-clear': GObject.ParamSpec.boolean(
|
'can-clear': GObject.ParamSpec.boolean(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user