[MessageTray] Merge Notification and NotificationBox

This will (eventually) give sources more control over the form of
notifications

https://bugzilla.gnome.org/show_bug.cgi?id=606979
This commit is contained in:
Dan Winship 2010-02-01 15:23:49 -05:00
parent ed36517615
commit d20d815b45
2 changed files with 47 additions and 61 deletions

View File

@ -26,53 +26,40 @@ const MessageTrayState = {
TRAY_ONLY: 3 // neither notifiations nor summary are visible, only tray TRAY_ONLY: 3 // neither notifiations nor summary are visible, only tray
}; };
function _cleanMarkup(text) {
// Support <b>, <i>, and <u>, escape anything else
// so it displays as raw markup.
return text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, "&lt;$1");
}
// Notification: // Notification:
// @source: the notification's Source // @source: the notification's Source
// @icon: a ClutterActor for the notification's icon
// @title: the title // @title: the title
// @banner: the banner text // @banner: the banner text
// @body: the body text, or %null // @body: the body text, or %null
// //
// Creates a notification. In banner mode, it will show // Creates a notification. In banner mode, it will show
// @icon, @title (in bold) and @banner, all on a single line // @source's icon, @title (in bold) and @banner, all on a single line
// (with @banner ellipsized if necessary). If @body is not %null, then // (with @banner ellipsized if necessary). If @body is not %null, then
// the notification will be expandable. In expanded mode, it will show // the notification will be expandable. In expanded mode, it will show
// just @icon and @title (in bold) on the first line, and @body on // just the icon and @title (in bold) on the first line, and @body on
// multiple lines underneath. // multiple lines underneath.
function Notification(source, icon, title, banner, body) { function Notification(source, title, banner, body) {
this._init(source, icon, title, banner, body); this._init(source, title, banner, body);
} }
Notification.prototype = { Notification.prototype = {
_init: function(source, icon, title, banner, body) { _init: function(source, title, banner, body) {
this.source = source; this.source = source;
this.icon = icon;
this.title = title ? this._cleanMarkup(title.replace('\n', ' ')) : '';
this.banner = banner ? this._cleanMarkup(banner.replace('\n', ' ')) : '';
this.body = body ? this._cleanMarkup(body) : null;
},
_cleanMarkup: function(text) {
// Support <b>, <i>, and <u>, escape anything else
// so it displays as raw markup.
return text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, "&lt;$1");
}
};
function NotificationBox() {
this._init();
}
NotificationBox.prototype = {
_init: function() {
this.actor = new St.Table({ name: 'notification' }); this.actor = new St.Table({ name: 'notification' });
this._iconBox = new St.Bin(); let icon = source.createIcon(ICON_SIZE);
this.actor.add(this._iconBox, { row: 0, this.actor.add(icon, { row: 0,
col: 0, col: 0,
x_expand: false, x_expand: false,
y_expand: false, y_expand: false,
y_fill: false }); y_fill: false });
// The first line should have the title, followed by the // The first line should have the title, followed by the
// banner text, but ellipsized if they won't both fit. We can't // banner text, but ellipsized if they won't both fit. We can't
@ -89,36 +76,33 @@ NotificationBox.prototype = {
y_fill: false }); y_fill: false });
this._titleText = new St.Label(); this._titleText = new St.Label();
title = title ? _cleanMarkup(title.replace('\n', ' ')) : '';
this._titleText.clutter_text.set_markup('<b>' + title + '</b>');
this._bannerBox.add_actor(this._titleText); this._bannerBox.add_actor(this._titleText);
this._bannerText = new St.Label(); this._bannerText = new St.Label();
banner = banner ? _cleanMarkup(banner.replace('\n', ' ')) : '';
this._bannerText.clutter_text.set_markup(banner);
this._bannerBox.add_actor(this._bannerText); this._bannerBox.add_actor(this._bannerText);
this._bodyText = new St.Label(); this._bodyText = new St.Label();
this._bodyText.clutter_text.line_wrap = true; this._bodyText.clutter_text.line_wrap = true;
this._bodyText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; this._bodyText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.actor.add(this._bodyText, { row: 1, if (body) {
col: 1 }); body = _cleanMarkup(body);
}, this._bodyText.clutter_text.set_markup(body);
setContent: function(notification) {
this.notification = notification;
this._iconBox.child = notification.icon;
this._titleText.clutter_text.set_markup('<b>' + notification.title + '</b>');
this._bannerText.clutter_text.set_markup(notification.banner);
if (notification.body) {
this._bodyText.clutter_text.set_markup(notification.body);
this._canPopOut = true; this._canPopOut = true;
} else { } else {
// If there's no body, then normally we wouldn't do pop-out. // If there's no body, then normally we wouldn't do
// But if title+banner is too wide for the notification, then // pop-out. But if title+banner is too wide for the
// we'd need to pop out to show the full banner. So we set up // notification, then we'd need to pop out to show the
// bodyText with that now. // full banner. So we set up bodyText with that now.
this._bodyText.clutter_text.set_markup(notification.banner); this._bodyText.clutter_text.set_markup(banner);
this._canPopOut = false; this._canPopOut = false;
} }
this.actor.add(this._bodyText, { row: 1,
col: 1 });
}, },
_bannerBoxGetPreferredWidth: function(actor, forHeight, alloc) { _bannerBoxGetPreferredWidth: function(actor, forHeight, alloc) {
@ -202,10 +186,8 @@ Source.prototype = {
throw new Error('no implementation of createIcon in ' + this); throw new Error('no implementation of createIcon in ' + this);
}, },
notify: function(title, banner, body) { notify: function(notification) {
this.notification = new Notification(this, this.createIcon(ICON_SIZE), this.emit('notify', notification);
title, banner, body);
this.emit('notify');
}, },
clicked: function() { clicked: function() {
@ -230,9 +212,8 @@ MessageTray.prototype = {
this._notificationBin = new St.Bin({ reactive: true }); this._notificationBin = new St.Bin({ reactive: true });
this.actor.add(this._notificationBin); this.actor.add(this._notificationBin);
this._notificationBin.hide(); this._notificationBin.hide();
this._notificationBox = new NotificationBox();
this._notificationBin.child = this._notificationBox.actor;
this._notificationQueue = []; this._notificationQueue = [];
this._notification = null;
this._summaryBin = new St.BoxLayout(); this._summaryBin = new St.BoxLayout();
this.actor.add(this._summaryBin); this.actor.add(this._summaryBin);
@ -315,7 +296,7 @@ MessageTray.prototype = {
// Update state if we are showing a notification from the removed source // Update state if we are showing a notification from the removed source
if (this._state == MessageTrayState.NOTIFICATION && if (this._state == MessageTrayState.NOTIFICATION &&
this._notificationBox.notification.source == source) this._notification.source == source)
this._updateState(); this._updateState();
this._summary.remove_actor(this._icons[source.id]); this._summary.remove_actor(this._icons[source.id]);
@ -327,8 +308,8 @@ MessageTray.prototype = {
return this._sources[id]; return this._sources[id];
}, },
_onNotify: function(source) { _onNotify: function(source, notification) {
this._notificationQueue.push(source.notification); this._notificationQueue.push(notification);
if (this._state == MessageTrayState.HIDDEN) if (this._state == MessageTrayState.HIDDEN)
this._updateState(); this._updateState();
@ -346,7 +327,7 @@ MessageTray.prototype = {
if (this._state == MessageTrayState.HIDDEN) if (this._state == MessageTrayState.HIDDEN)
this._updateState(); this._updateState();
else if (this._state == MessageTrayState.NOTIFICATION) { else if (this._state == MessageTrayState.NOTIFICATION) {
if (this._notificationBox.popOut()) { if (this._notification.popOut()) {
Tweener.addTween(this._notificationBin, Tweener.addTween(this._notificationBin,
{ y: this.actor.height - this._notificationBin.height, { y: this.actor.height - this._notificationBin.height,
time: ANIMATION_TIME, time: ANIMATION_TIME,
@ -456,7 +437,8 @@ MessageTray.prototype = {
}, },
_showNotification: function() { _showNotification: function() {
this._notificationBox.setContent(this._notificationQueue.shift()); this._notification = this._notificationQueue.shift();
this._notificationBin.child = this._notification.actor;
this._notificationBin.opacity = 0; this._notificationBin.opacity = 0;
this._notificationBin.y = this.actor.height; this._notificationBin.y = this.actor.height;
@ -470,7 +452,7 @@ MessageTray.prototype = {
}, },
_hideNotification: function() { _hideNotification: function() {
this._notificationBox.popIn(); this._notification.popIn();
Tweener.addTween(this._notificationBin, Tweener.addTween(this._notificationBin,
{ y: this.actor.height, { y: this.actor.height,
@ -479,6 +461,8 @@ MessageTray.prototype = {
transition: "easeOutQuad", transition: "easeOutQuad",
onComplete: Lang.bind(this, function() { onComplete: Lang.bind(this, function() {
this._notificationBin.hide(); this._notificationBin.hide();
this._notificationBin.child = null;
this._notification = null;
})}); })});
}, },

View File

@ -122,7 +122,9 @@ NotificationDaemon.prototype = {
} }
summary = GLib.markup_escape_text(summary, -1); summary = GLib.markup_escape_text(summary, -1);
source.notify(summary, body);
let notification = new MessageTray.Notification(source, summary, body);
source.notify(notification);
return id; return id;
}, },