Combine notifications and the summary mode in a single message tray
We should either be showing the message tray in the notification or the summary mode. This is best achieved if the message tray actor contains both, and either one is shown at any given time. Queue notifications so that each queued notification is shown when the previous one times out or when the user is done interacting with the message tray and moves the mouse away from it. (In the future, we will have some sort of an indication that there are queued notifications and a way to have the next notification displayed without moving the mouse away from the message tray.)
This commit is contained in:
parent
09653fbaf6
commit
4ab513ca77
@ -473,25 +473,24 @@ StTooltip {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Message Tray */
|
/* Message Tray */
|
||||||
|
#message-tray {
|
||||||
|
background-gradient-direction: vertical;
|
||||||
|
background-gradient-start: rgba(0,0,0,0.01);
|
||||||
|
background-gradient-end: rgba(0,0,0,0.95);
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
#notification {
|
#notification {
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background: rgba(0,0,0,0.9);
|
background: rgba(0,0,0,0.9);
|
||||||
border: 1px solid rgba(128,128,128,0.45);
|
|
||||||
color: white;
|
color: white;
|
||||||
padding: 4px;
|
padding: 2px 10px;
|
||||||
spacing: 10px;
|
spacing: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#message-tray {
|
#summary-mode {
|
||||||
border-radius: 5px;
|
|
||||||
background: rgba(0,0,0,0.9);
|
|
||||||
border: 1px solid rgba(128,128,128,0.45);
|
|
||||||
padding: 4px;
|
|
||||||
height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#message-tray-inner {
|
|
||||||
spacing: 10px;
|
spacing: 10px;
|
||||||
|
padding: 2px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* App Switcher */
|
/* App Switcher */
|
||||||
|
@ -16,11 +16,22 @@ const MESSAGE_TRAY_TIMEOUT = 0.2;
|
|||||||
|
|
||||||
const ICON_SIZE = 24;
|
const ICON_SIZE = 24;
|
||||||
|
|
||||||
function Notification() {
|
function Notification(icon, text) {
|
||||||
this._init();
|
this._init(icon, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
Notification.prototype = {
|
Notification.prototype = {
|
||||||
|
_init: function(icon, text) {
|
||||||
|
this.icon = icon;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function NotificationBox() {
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationBox.prototype = {
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new St.BoxLayout({ name: 'notification' });
|
this.actor = new St.BoxLayout({ name: 'notification' });
|
||||||
|
|
||||||
@ -29,55 +40,11 @@ Notification.prototype = {
|
|||||||
|
|
||||||
this._text = new St.Label();
|
this._text = new St.Label();
|
||||||
this.actor.add(this._text, { expand: true, x_fill: false, y_fill: false, y_align: St.Align.MIDDLE });
|
this.actor.add(this._text, { expand: true, x_fill: false, y_fill: false, y_align: St.Align.MIDDLE });
|
||||||
|
|
||||||
Main.chrome.addActor(this.actor, { affectsStruts: false });
|
|
||||||
|
|
||||||
let primary = global.get_primary_monitor();
|
|
||||||
this.actor.y = primary.height;
|
|
||||||
|
|
||||||
this._hideTimeoutId = 0;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
show: function(icon, text) {
|
setContent: function(notification) {
|
||||||
let primary = global.get_primary_monitor();
|
this._iconBox.child = notification.icon;
|
||||||
|
this._text.text = notification.text;
|
||||||
if (this._hideTimeoutId > 0)
|
|
||||||
Mainloop.source_remove(this._hideTimeoutId);
|
|
||||||
this._hideTimeoutId = Mainloop.timeout_add(NOTIFICATION_TIMEOUT * 1000, Lang.bind(this, this.hide));
|
|
||||||
|
|
||||||
this._iconBox.child = icon;
|
|
||||||
this._text.text = text;
|
|
||||||
|
|
||||||
this.actor.x = Math.round((primary.width - this.actor.width) / 2);
|
|
||||||
this.actor.show();
|
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ y: primary.height - this.actor.height,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: "easeOutQuad"
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
hide: function() {
|
|
||||||
let primary = global.get_primary_monitor();
|
|
||||||
this._hideTimeoutId = 0;
|
|
||||||
|
|
||||||
Tweener.addTween(this.actor,
|
|
||||||
{ y: primary.height,
|
|
||||||
time: ANIMATION_TIME,
|
|
||||||
transition: "easeOutQuad",
|
|
||||||
onComplete: this.hideComplete,
|
|
||||||
onCompleteScope: this
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
hideComplete: function() {
|
|
||||||
if (this._iconBox.child)
|
|
||||||
this._iconBox.child.destroy();
|
|
||||||
|
|
||||||
// Don't hide the notification if we are showing a new one.
|
|
||||||
if (this._hideTimeoutId == 0)
|
|
||||||
this.actor.hide();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -99,7 +66,7 @@ Source.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
notify: function(text) {
|
notify: function(text) {
|
||||||
Main.notificationPopup.show(this.createIcon(ICON_SIZE), text);
|
Main.messageTray.showNotification(new Notification(this.createIcon(ICON_SIZE), text));
|
||||||
},
|
},
|
||||||
|
|
||||||
clicked: function() {
|
clicked: function() {
|
||||||
@ -118,10 +85,8 @@ function MessageTray() {
|
|||||||
|
|
||||||
MessageTray.prototype = {
|
MessageTray.prototype = {
|
||||||
_init: function() {
|
_init: function() {
|
||||||
this.actor = new St.Bin({ name: 'message-tray',
|
this.actor = new St.BoxLayout({ name: 'message-tray',
|
||||||
reactive: true,
|
reactive: true });
|
||||||
x_align: St.Align.END });
|
|
||||||
Main.chrome.addActor(this.actor, { affectsStruts: false });
|
|
||||||
|
|
||||||
let primary = global.get_primary_monitor();
|
let primary = global.get_primary_monitor();
|
||||||
this.actor.x = 0;
|
this.actor.x = 0;
|
||||||
@ -129,6 +94,17 @@ MessageTray.prototype = {
|
|||||||
|
|
||||||
this.actor.width = primary.width;
|
this.actor.width = primary.width;
|
||||||
|
|
||||||
|
this._summaryBin = new St.Bin({ x_align: St.Align.END });
|
||||||
|
this.actor.add(this._summaryBin, { expand: true });
|
||||||
|
this._summaryBin.hide();
|
||||||
|
|
||||||
|
this._notificationBox = new NotificationBox();
|
||||||
|
this._notificationQueue = [];
|
||||||
|
this.actor.add(this._notificationBox.actor);
|
||||||
|
this._notificationBox.actor.hide();
|
||||||
|
|
||||||
|
Main.chrome.addActor(this.actor, { affectsStruts: false });
|
||||||
|
|
||||||
this.actor.connect('enter-event',
|
this.actor.connect('enter-event',
|
||||||
Lang.bind(this, this._onMessageTrayEntered));
|
Lang.bind(this, this._onMessageTrayEntered));
|
||||||
this.actor.connect('leave-event',
|
this.actor.connect('leave-event',
|
||||||
@ -136,9 +112,9 @@ MessageTray.prototype = {
|
|||||||
this._isShowing = false;
|
this._isShowing = false;
|
||||||
this.actor.show();
|
this.actor.show();
|
||||||
|
|
||||||
this._tray = new St.BoxLayout({ name: 'message-tray-inner' });
|
this._summary = new St.BoxLayout({ name: 'summary-mode' });
|
||||||
this.actor.child = this._tray;
|
this._summaryBin.child = this._summary;
|
||||||
this._tray.expand = true;
|
|
||||||
this._sources = {};
|
this._sources = {};
|
||||||
this._icons = {};
|
this._icons = {};
|
||||||
},
|
},
|
||||||
@ -155,7 +131,7 @@ MessageTray.prototype = {
|
|||||||
|
|
||||||
let iconBox = new St.Bin({ reactive: true });
|
let iconBox = new St.Bin({ reactive: true });
|
||||||
iconBox.child = source.createIcon(ICON_SIZE);
|
iconBox.child = source.createIcon(ICON_SIZE);
|
||||||
this._tray.insert_actor(iconBox, 0);
|
this._summary.insert_actor(iconBox, 0);
|
||||||
this._icons[source.id] = iconBox;
|
this._icons[source.id] = iconBox;
|
||||||
this._sources[source.id] = source;
|
this._sources[source.id] = source;
|
||||||
|
|
||||||
@ -174,7 +150,7 @@ MessageTray.prototype = {
|
|||||||
if (!this.contains(source))
|
if (!this.contains(source))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._tray.remove_actor(this._icons[source.id]);
|
this._summary.remove_actor(this._icons[source.id]);
|
||||||
delete this._icons[source.id];
|
delete this._icons[source.id];
|
||||||
delete this._sources[source.id];
|
delete this._sources[source.id];
|
||||||
},
|
},
|
||||||
@ -184,12 +160,29 @@ MessageTray.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_onMessageTrayEntered: function() {
|
_onMessageTrayEntered: function() {
|
||||||
|
// Don't hide the message tray after a timeout if the user has moved the mouse over it.
|
||||||
|
// We might have a timeout in place if the user moved the mouse away from the message tray for a very short period of time
|
||||||
|
// or if we are showing a notification.
|
||||||
if (this._hideTimeoutId > 0)
|
if (this._hideTimeoutId > 0)
|
||||||
Mainloop.source_remove(this._hideTimeoutId);
|
Mainloop.source_remove(this._hideTimeoutId);
|
||||||
|
|
||||||
if (this._isShowing)
|
if (this._isShowing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// If the message tray was not already showing, we'll show it in the summary mode.
|
||||||
|
this._summaryBin.show();
|
||||||
|
this._show();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onMessageTrayLeft: function() {
|
||||||
|
if (!this._isShowing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// We wait just a little before hiding the message tray in case the user will quickly move the mouse back over it.
|
||||||
|
this._hideTimeoutId = Mainloop.timeout_add(MESSAGE_TRAY_TIMEOUT * 1000, Lang.bind(this, this._hide));
|
||||||
|
},
|
||||||
|
|
||||||
|
_show: function() {
|
||||||
this._isShowing = true;
|
this._isShowing = true;
|
||||||
let primary = global.get_primary_monitor();
|
let primary = global.get_primary_monitor();
|
||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
@ -199,15 +192,7 @@ MessageTray.prototype = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_onMessageTrayLeft: function() {
|
|
||||||
if (!this._isShowing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._hideTimeoutId = Mainloop.timeout_add(MESSAGE_TRAY_TIMEOUT * 1000, Lang.bind(this, this._hide));
|
|
||||||
},
|
|
||||||
|
|
||||||
_hide: function() {
|
_hide: function() {
|
||||||
this._isShowing = false;
|
|
||||||
this._hideTimeoutId = 0;
|
this._hideTimeoutId = 0;
|
||||||
|
|
||||||
let primary = global.get_primary_monitor();
|
let primary = global.get_primary_monitor();
|
||||||
@ -215,9 +200,36 @@ MessageTray.prototype = {
|
|||||||
Tweener.addTween(this.actor,
|
Tweener.addTween(this.actor,
|
||||||
{ y: primary.height - 1,
|
{ y: primary.height - 1,
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
transition: "easeOutQuad"
|
transition: "easeOutQuad",
|
||||||
|
onComplete: this._hideComplete,
|
||||||
|
onCompleteScope: this
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
};
|
|
||||||
|
|
||||||
|
_hideComplete: function() {
|
||||||
|
this._isShowing = false;
|
||||||
|
this._summaryBin.hide();
|
||||||
|
this._notificationBox.actor.hide();
|
||||||
|
if (this._notificationQueue.length > 0)
|
||||||
|
this.showNotification(this._notificationQueue.shift());
|
||||||
|
},
|
||||||
|
|
||||||
|
showNotification: function(notification) {
|
||||||
|
if (this._isShowing) {
|
||||||
|
this._notificationQueue.push(notification);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._notificationBox.setContent(notification);
|
||||||
|
|
||||||
|
this._notificationBox.actor.x = Math.round((this.actor.width - this._notificationBox.actor.width) / 2);
|
||||||
|
this._notificationBox.actor.show();
|
||||||
|
|
||||||
|
// Because we set up the timeout before we do the animation, we add ANIMATION_TIME to NOTIFICATION_TIMEOUT, so that
|
||||||
|
// NOTIFICATION_TIMEOUT represents the time the notifiation is fully shown.
|
||||||
|
this._hideTimeoutId = Mainloop.timeout_add((NOTIFICATION_TIMEOUT + ANIMATION_TIME) * 1000, Lang.bind(this, this._hide));
|
||||||
|
|
||||||
|
this._show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user