[gnome-shell/wip/fmuellner/chat-notifications: 4/5] telepathyClient: Provide a custom banner implementation
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/fmuellner/chat-notifications: 4/5] telepathyClient: Provide a custom banner implementation
- Date: Tue, 17 Mar 2015 05:28:34 +0000 (UTC)
commit ff68e268b2da4350b974b1a870ba5c43a7f236e6
Author: Florian Müllner <fmuellner gnome org>
Date: Tue Feb 17 04:19:07 2015 +0100
telepathyClient: Provide a custom banner implementation
Since we stopped special-casing chat notifications to use the old
notification actor, we need to provide a notification banner to
maintain the inline chat functionality, so split out the UI from
the existing ChatNotification class.
data/theme/gnome-shell-high-contrast.css | 13 +-
data/theme/gnome-shell.css | 13 +-
js/ui/components/telepathyClient.js | 289 ++++++++++++++++++++----------
3 files changed, 207 insertions(+), 108 deletions(-)
---
diff --git a/data/theme/gnome-shell-high-contrast.css b/data/theme/gnome-shell-high-contrast.css
index aad3777..2642d1c 100644
--- a/data/theme/gnome-shell-high-contrast.css
+++ b/data/theme/gnome-shell-high-contrast.css
@@ -1254,11 +1254,17 @@ StScrollBar {
.secondary-icon {
icon-size: 1.09em; }
+.chat-body {
+ spacing: 5px; }
+
+.chat-response {
+ margin: 5px; }
+
.chat-log-message {
color: #e6e6e6; }
-.chat-empty-line {
- font-size: 4px; }
+.chat-new-group {
+ padding-top: 1em; }
.chat-received {
padding-left: 4px; }
@@ -1282,9 +1288,6 @@ StScrollBar {
padding-left: 0;
padding-right: 4px; }
-.chat-notification-scrollview {
- max-height: 22em; }
-
.hotplug-transient-box {
spacing: 6px;
padding: 2px 72px 2px 12px; }
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 0267213..55ed702 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -1254,11 +1254,17 @@ StScrollBar {
.secondary-icon {
icon-size: 1.09em; }
+.chat-body {
+ spacing: 5px; }
+
+.chat-response {
+ margin: 5px; }
+
.chat-log-message {
color: #d6d6d1; }
-.chat-empty-line {
- font-size: 4px; }
+.chat-new-group {
+ padding-top: 1em; }
.chat-received {
padding-left: 4px; }
@@ -1282,9 +1288,6 @@ StScrollBar {
padding-left: 0;
padding-right: 4px; }
-.chat-notification-scrollview {
- max-height: 22em; }
-
.hotplug-transient-box {
spacing: 6px;
padding: 2px 72px 2px 12px; }
diff --git a/js/ui/components/telepathyClient.js b/js/ui/components/telepathyClient.js
index ddcddfe..ba2f3b4 100644
--- a/js/ui/components/telepathyClient.js
+++ b/js/ui/components/telepathyClient.js
@@ -3,6 +3,7 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
+const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
@@ -31,6 +32,8 @@ const SCROLLBACK_HISTORY_LINES = 10;
// See Notification._onEntryChanged
const COMPOSING_STOP_TIMEOUT = 5;
+const CHAT_EXPAND_LINES = 12;
+
const NotificationDirection = {
SENT: 'chat-sent',
RECEIVED: 'chat-received'
@@ -273,13 +276,13 @@ const ChatSource = new Lang.Class({
this._notification = new ChatNotification(this);
this._notification.connect('activated', Lang.bind(this, this.open));
- this._notification.setUrgency(MessageTray.Urgency.HIGH);
+ this._notification.connect('updated', Lang.bind(this,
+ function() {
+ if (this._banner && this._banner.expanded)
+ this._ackMessages();
+ }));
this._notifyTimeoutId = 0;
- // We ack messages when the user expands the new notification or views the summary
- // notification, in which case the notification is also expanded.
- this._notification.connect('expanded', Lang.bind(this, this._ackMessages));
-
this._presence = contact.get_presence_type();
this._sentId = this._channel.connect('message-sent', Lang.bind(this, this._messageSent));
@@ -301,6 +304,20 @@ const ChatSource = new Lang.Class({
return new MessageTray.NotificationApplicationPolicy('empathy');
},
+ createBanner: function() {
+ this._banner = new ChatNotificationBanner(this._notification);
+
+ // We ack messages when the user expands the new notification
+ let id = this._banner.connect('expanded', Lang.bind(this, this._ackMessages));
+ this._banner.actor.connect('destroy', Lang.bind(this,
+ function() {
+ this._banner.disconnect(id);
+ this._banner = null;
+ }));
+
+ return this._banner;
+ },
+
_updateAlias: function() {
let oldAlias = this.title;
let newAlias = this._contact.get_alias();
@@ -354,7 +371,7 @@ const ChatSource = new Lang.Class({
this.iconUpdated();
this._notification.update(this._notification.title,
this._notification.bannerBodyText,
- { gicon: this.getIcon(), customContent: true });
+ { gicon: this.getIcon() });
},
open: function() {
@@ -550,7 +567,7 @@ const ChatSource = new Lang.Class({
_presenceChanged: function (contact, presence, status, message) {
this._notification.update(this._notification.title,
this._notification.bannerBodyText,
- { customContent: true, secondaryGIcon: this.getSecondaryIcon() });
+ { secondaryGIcon: this.getSecondaryIcon() });
},
_pendingRemoved: function(channel, message) {
@@ -574,42 +591,13 @@ const ChatNotification = new Lang.Class({
Extends: MessageTray.Notification,
_init: function(source) {
- this.parent(source, source.title, null, { customContent: true, secondaryGIcon:
source.getSecondaryIcon() });
+ this.parent(source, source.title, null,
+ { secondaryGIcon: source.getSecondaryIcon() });
+ this.setUrgency(MessageTray.Urgency.HIGH);
this.setResident(true);
- this._responseEntry = new St.Entry({ style_class: 'chat-response',
- can_focus: true });
- this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated));
- this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
- this.setActionArea(this._responseEntry);
-
- this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() {
- this.focused = true;
- }));
- this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() {
- this.focused = false;
- this.emit('unfocused');
- }));
-
- this._createScrollArea();
- this._lastGroup = null;
-
- // Keep track of the bottom position for the current adjustment and
- // force a scroll to the bottom if things change while we were at the
- // bottom
- this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value;
- this._scrollArea.add_style_class_name('chat-notification-scrollview');
- this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
- if (adjustment.value == this._oldMaxScrollValue)
- this.scrollTo(St.Side.BOTTOM);
- this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size);
- }));
-
- this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text });
-
- this._history = [];
+ this.messages = [];
this._timestampTimeoutId = 0;
- this._composingTimeoutId = 0;
},
/**
@@ -635,10 +623,8 @@ const ChatNotification = new Lang.Class({
styles.push('chat-action');
}
- if (message.direction == NotificationDirection.RECEIVED) {
- this.update(this.source.title, messageBody, { customContent: true,
- bannerMarkup: true });
- }
+ if (message.direction == NotificationDirection.RECEIVED)
+ this.update(this.source.title, messageBody, { bannerMarkup: true });
let group = (message.direction == NotificationDirection.RECEIVED ?
'received' : 'sent');
@@ -651,10 +637,10 @@ const ChatNotification = new Lang.Class({
},
_filterMessages: function() {
- if (this._history.length < 1)
+ if (this.messages.length < 1)
return;
- let lastMessageTime = this._history[0].time;
+ let lastMessageTime = this.messages[0].timestamp;
let currentTime = (Date.now() / 1000);
// Keep the scrollback from growing too long. If the most
@@ -666,12 +652,12 @@ const ChatNotification = new Lang.Class({
let maxLength = (lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME) ?
SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH;
- let filteredHistory = this._history.filter(function(item) { return item.realMessage });
+ let filteredHistory = this.messages.filter(function(item) { return item.realMessage });
if (filteredHistory.length > maxLength) {
let lastMessageToKeep = filteredHistory[maxLength];
- let expired = this._history.splice(this._history.indexOf(lastMessageToKeep));
+ let expired = this.messages.splice(this.messages.indexOf(lastMessageToKeep));
for (let i = 0; i < expired.length; i++)
- expired[i].actor.destroy();
+ this.emit('message-removed', expired[i]);
}
},
@@ -684,7 +670,6 @@ const ChatNotification = new Lang.Class({
* styles: Style class names for the message to have.
* timestamp: The timestamp of the message.
* noTimestamp: suppress timestamp signal?
- * childProps: props to add the actor with.
*/
_append: function(props) {
let currentTime = (Date.now() / 1000);
@@ -692,44 +677,22 @@ const ChatNotification = new Lang.Class({
group: null,
styles: [],
timestamp: currentTime,
- noTimestamp: false,
- childProps: null });
+ noTimestamp: false });
// Reset the old message timeout
if (this._timestampTimeoutId)
Mainloop.source_remove(this._timestampTimeoutId);
- let highlighter = new MessageTray.URLHighlighter(props.body,
- true, // line wrap?
- true); // allow markup?
-
- let body = highlighter.actor;
-
- let styles = props.styles;
- for (let i = 0; i < styles.length; i++)
- body.add_style_class_name(styles[i]);
-
- let group = props.group;
- if (group != this._lastGroup) {
- this._lastGroup = group;
- let emptyLine = new St.Label({ style_class: 'chat-empty-line' });
- this.addActor(emptyLine);
- this._history.unshift({ actor: emptyLine, time: timestamp,
- realMessage: false });
- }
+ let message = { realMessage: props.group != 'meta',
+ showTimestamp: false };
+ Lang.copyProperties(props, message);
+ delete message.noTimestamp;
- let lineBox = new St.BoxLayout({ vertical: false });
- lineBox.add(body, props.childProps);
- this.addActor(lineBox);
- this._lastMessageBox = lineBox;
-
- this.updated();
-
- let timestamp = props.timestamp;
- this._history.unshift({ actor: lineBox, time: timestamp,
- realMessage: group != 'meta' });
+ this.messages.unshift(message);
+ this.emit('message-added', message);
if (!props.noTimestamp) {
+ let timestamp = props.timestamp;
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) {
this.appendTimestamp();
} else {
@@ -748,15 +711,8 @@ const ChatNotification = new Lang.Class({
appendTimestamp: function() {
this._timestampTimeoutId = 0;
- let lastMessageTime = this._history[0].time;
- let lastMessageDate = new Date(lastMessageTime * 1000);
-
- let timeLabel = Util.createTimeLabel(lastMessageDate);
- timeLabel.style_class = 'chat-meta-message';
- timeLabel.x_expand = timeLabel.y_expand = true;
- timeLabel.x_align = timeLabel.y_align = Clutter.ActorAlign.END;
-
- this._lastMessageBox.add_actor(timeLabel);
+ this.messages[0].showTimestamp = true;
+ this.emit('timestamp-changed', this.messages[0]);
this._filterMessages();
@@ -771,13 +727,150 @@ const ChatNotification = new Lang.Class({
IM name. */
let message = '<i>' + _("%s is now known as %s").format(oldAlias, newAlias) + '</i>';
- let label = this._append({ body: message,
- group: 'meta',
- styles: ['chat-meta-message'] });
-
- this.update(newAlias, null, { customContent: true });
+ this._append({ body: message,
+ group: 'meta',
+ styles: ['chat-meta-message'] });
this._filterMessages();
+ }
+});
+
+const ChatLineBox = new Lang.Class({
+ Name: 'ChatLineBox',
+ Extends: St.BoxLayout,
+
+ vfunc_get_preferred_height: function(forWidth) {
+ let [, natHeight] = this.parent(forWidth);
+ return [natHeight, natHeight];
+ }
+});
+
+const ChatNotificationBanner = new Lang.Class({
+ Name: 'ChatNotificationBanner',
+ Extends: MessageTray.NotificationBanner,
+
+ _init: function(notification) {
+ this.parent(notification);
+
+ this._responseEntry = new St.Entry({ style_class: 'chat-response',
+ x_expand: true,
+ can_focus: true });
+ this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated));
+ this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged));
+ this.setActionArea(this._responseEntry);
+
+ this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() {
+ this.focused = true;
+ }));
+ this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() {
+ this.focused = false;
+ this.emit('unfocused');
+ }));
+
+ this._scrollArea = new St.ScrollView({ style_class: 'chat-scrollview vfade',
+ vscrollbar_policy: Gtk.PolicyType.AUTOMATIC,
+ hscrollbar_policy: Gtk.PolicyType.NEVER,
+ visible: this.expanded });
+ this._contentArea = new St.BoxLayout({ style_class: 'chat-body',
+ vertical: true });
+ this._scrollArea.add_actor(this._contentArea);
+
+ this.setExpandedBody(this._scrollArea);
+ this.setExpandedLines(CHAT_EXPAND_LINES);
+
+ this._lastGroup = null;
+
+ // Keep track of the bottom position for the current adjustment and
+ // force a scroll to the bottom if things change while we were at the
+ // bottom
+ this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value;
+ this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
+ if (adjustment.value == this._oldMaxScrollValue)
+ this.scrollTo(St.Side.BOTTOM);
+ this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size);
+ }));
+
+ this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text });
+
+ this._composingTimeoutId = 0;
+
+ this._messageActors = new Map();
+
+ this._messageAddedId = this.notification.connect('message-added',
+ Lang.bind(this, function(n, message) {
+ this._addMessage(message);
+ }));
+ this._messageRemovedId = this.notification.connect('message-removed',
+ Lang.bind(this, function(n, message) {
+ let actor = this._messageActors.get(message);
+ if (this._messageActors.delete(message))
+ actor.destroy();
+ }));
+ this._timestampChangedId = this.notification.connect('timestamp-changed',
+ Lang.bind(this, function(n, message) {
+ this._updateTimestamp(message);
+ }));
+
+ for (let i = this.notification.messages.length - 1; i >= 0; i--)
+ this._addMessage(this.notification.messages[i]);
+ },
+
+ _onDestroy: function() {
+ this.parent();
+ this.notification.disconnect(this._messageAddedId);
+ this.notification.disconnect(this._messageRemovedId);
+ this.notification.disconnect(this._timestampChangedId);
+ },
+
+ scrollTo: function(side) {
+ let adjustment = this._scrollArea.vscroll.adjustment;
+ if (side == St.Side.TOP)
+ adjustment.value = adjustment.lower;
+ else if (side == St.Side.BOTTOM)
+ adjustment.value = adjustment.upper;
+ },
+
+ _addMessage: function(message) {
+ let highlighter = new Calendar.URLHighlighter(message.body, true, true);
+ let body = highlighter.actor;
+
+ let styles = message.styles;
+ for (let i = 0; i < styles.length; i++)
+ body.add_style_class_name(styles[i]);
+
+ let group = message.group;
+ if (group != this._lastGroup) {
+ this._lastGroup = group;
+ body.add_style_class_name('chat-new-group');
+ }
+
+ let lineBox = new ChatLineBox();
+ lineBox.add(body);
+ this._contentArea.add_actor(lineBox);
+ this._messageActors.set(message, lineBox);
+
+ this._updateTimestamp(message);
+ },
+
+ _updateTimestamp: function(message) {
+ let actor = this._messageActors.get(message);
+ if (!actor)
+ return;
+
+ while (actor.get_n_children() > 1)
+ actor.get_child_at_index(1).destroy();
+
+ if (message.showTimestamp) {
+ let lastMessageTime = message.timestamp;
+ let lastMessageDate = new Date(lastMessageTime * 1000);
+
+ let timeLabel = Util.createTimeLabel(lastMessageDate);
+ timeLabel.style_class = 'chat-meta-message';
+ timeLabel.x_expand = timeLabel.y_expand = true;
+ timeLabel.x_align = timeLabel.y_align = Clutter.ActorAlign.END;
+
+ actor.add_actor(timeLabel);
+ }
},
_onEntryActivated: function() {
@@ -790,13 +883,13 @@ const ChatNotification = new Lang.Class({
// Telepathy sends out the Sent signal for us.
// see Source._messageSent
this._responseEntry.set_text('');
- this.source.respond(text);
+ this.notification.source.respond(text);
},
_composingStopTimeout: function() {
this._composingTimeoutId = 0;
- this.source.setChatState(Tp.ChannelChatState.PAUSED);
+ this.notification.source.setChatState(Tp.ChannelChatState.PAUSED);
return GLib.SOURCE_REMOVE;
},
@@ -816,14 +909,14 @@ const ChatNotification = new Lang.Class({
}
if (text != '') {
- this.source.setChatState(Tp.ChannelChatState.COMPOSING);
+ this.notification.source.setChatState(Tp.ChannelChatState.COMPOSING);
this._composingTimeoutId = Mainloop.timeout_add_seconds(
COMPOSING_STOP_TIMEOUT,
Lang.bind(this, this._composingStopTimeout));
GLib.Source.set_name_by_id(this._composingTimeoutId, '[gnome-shell] this._composingStopTimeout');
} else {
- this.source.setChatState(Tp.ChannelChatState.ACTIVE);
+ this.notification.source.setChatState(Tp.ChannelChatState.ACTIVE);
}
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]