MessageTray: add right click menu for summary items

This provides straight forward controls for opening the corresponding
application or removing the summary item.

https://bugzilla.gnome.org/show_bug.cgi?id=617224
This commit is contained in:
Hellyna Ng 2011-02-12 03:43:01 +08:00 committed by Marina Zhurakhinskaya
parent 4282748483
commit 868bf5838d
6 changed files with 139 additions and 76 deletions

View File

@ -977,7 +977,7 @@ StTooltip StLabel {
padding-bottom: 8px; padding-bottom: 8px;
} }
.summary-notification-boxpointer { .summary-boxpointer {
-arrow-border-radius: 9px; -arrow-border-radius: 9px;
-arrow-background-color: rgba(0,0,0,0.9); -arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px; -arrow-border-width: 2px;
@ -986,12 +986,18 @@ StTooltip StLabel {
-arrow-rise: 15px; -arrow-rise: 15px;
} }
.summary-notification-boxpointer #notification { .summary-boxpointer #notification {
border-radius: 9px; border-radius: 9px;
background: rgba(0,0,0,0) !important; background: rgba(0,0,0,0) !important;
padding-bottom: 12px; padding-bottom: 12px;
} }
.summary-boxpointer #summary-right-click-menu {
font-size: 14px;
padding-top: 12px;
padding-bottom: 12px;
}
#notification-scrollview { #notification-scrollview {
max-height: 10em; max-height: 10em;
} }

View File

@ -15,6 +15,7 @@ const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession; const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main; const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const Params = imports.misc.params; const Params = imports.misc.params;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const Util = imports.misc.util; const Util = imports.misc.util;
@ -853,7 +854,7 @@ Source.prototype = {
this.notification = notification; this.notification = notification;
this._notificationClickedId = notification.connect('clicked', Lang.bind(this, this._notificationClicked)); this._notificationClickedId = notification.connect('clicked', Lang.bind(this, this.open));
this._notificationDestroyedId = notification.connect('destroy', Lang.bind(this, this._notificationDestroyedId = notification.connect('destroy', Lang.bind(this,
function () { function () {
if (this.notification == notification) { if (this.notification == notification) {
@ -881,7 +882,7 @@ Source.prototype = {
}, },
// Default implementation is to do nothing, but subclasses can override // Default implementation is to do nothing, but subclasses can override
_notificationClicked: function(notification) { open: function(notification) {
}, },
// Default implementation is to destroy this source, but subclasses can override // Default implementation is to destroy this source, but subclasses can override
@ -917,6 +918,27 @@ SummaryItem.prototype = {
this._sourceBox.add_actor(this._sourceIcon); this._sourceBox.add_actor(this._sourceIcon);
this._sourceBox.add_actor(this._sourceTitleBin, { expand: true }); this._sourceBox.add_actor(this._sourceTitleBin, { expand: true });
this.actor.child = this._sourceBox; this.actor.child = this._sourceBox;
this.rightClickMenu = new St.BoxLayout({ name: 'summary-right-click-menu',
vertical: true });
let item;
item = new PopupMenu.PopupMenuItem(_("Open"));
item.connect('activate', Lang.bind(this, function() {
source.open();
this.emit('right-click-menu-done-displaying');
}));
this.rightClickMenu.add(item.actor);
item = new PopupMenu.PopupMenuItem(_("Remove"));
item.connect('activate', Lang.bind(this, function() {
source.destroy();
this.emit('right-click-menu-done-displaying');
}));
this.rightClickMenu.add(item.actor);
let focusManager = St.FocusManager.get_for_stage(global.stage);
focusManager.add_group(this.rightClickMenu);
}, },
// getTitleNaturalWidth, getTitleWidth, and setTitleWidth include // getTitleNaturalWidth, getTitleWidth, and setTitleWidth include
@ -943,6 +965,7 @@ SummaryItem.prototype = {
this._sourceTitle.clutter_text.ellipsize = mode; this._sourceTitle.clutter_text.ellipsize = mode;
} }
}; };
Signals.addSignalMethods(SummaryItem.prototype);
function MessageTray() { function MessageTray() {
this._init(); this._init();
@ -980,17 +1003,19 @@ MessageTray.prototype = {
this._summaryMotionId = 0; this._summaryMotionId = 0;
this._summaryNotificationBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM, this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ reactive: true, { reactive: true,
track_hover: true }); track_hover: true });
this._summaryNotificationBoxPointer.actor.style_class = 'summary-notification-boxpointer'; this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
this.actor.add_actor(this._summaryNotificationBoxPointer.actor); this.actor.add_actor(this._summaryBoxPointer.actor);
this._summaryNotificationBoxPointer.actor.lower_bottom(); this._summaryBoxPointer.actor.lower_bottom();
this._summaryNotificationBoxPointer.actor.hide(); this._summaryBoxPointer.actor.hide();
this._summaryNotification = null; this._summaryNotification = null;
this._summaryNotificationClickedId = 0; this._summaryNotificationClickedId = 0;
this._summaryRightClickMenuClickedId = 0;
this._clickedSummaryItem = null; this._clickedSummaryItem = null;
this._clickedSummaryItemMouseButton = -1;
this._clickedSummaryItemAllocationChangedId = 0; this._clickedSummaryItemAllocationChangedId = 0;
this._expandedSummaryItem = null; this._expandedSummaryItem = null;
this._summaryItemTitleWidth = 0; this._summaryItemTitleWidth = 0;
@ -1004,7 +1029,7 @@ MessageTray.prototype = {
this._focusGrabber = new FocusGrabber(); this._focusGrabber = new FocusGrabber();
this._focusGrabber.connect('focus-grabbed', Lang.bind(this, this._focusGrabber.connect('focus-grabbed', Lang.bind(this,
function() { function() {
if (this._summaryNotification) if (this._summaryBoxPointer.bin.child)
this._lock(); this._lock();
})); }));
this._focusGrabber.connect('focus-ungrabbed', Lang.bind(this, this._unlock)); this._focusGrabber.connect('focus-ungrabbed', Lang.bind(this, this._unlock));
@ -1027,7 +1052,7 @@ MessageTray.prototype = {
this._notificationState = State.HIDDEN; this._notificationState = State.HIDDEN;
this._notificationTimeoutId = 0; this._notificationTimeoutId = 0;
this._notificationExpandedId = 0; this._notificationExpandedId = 0;
this._summaryNotificationState = State.HIDDEN; this._summaryBoxPointerState = State.HIDDEN;
this._summaryNotificationTimeoutId = 0; this._summaryNotificationTimeoutId = 0;
this._summaryNotificationExpandedId = 0; this._summaryNotificationExpandedId = 0;
this._overviewVisible = Main.overview.visible; this._overviewVisible = Main.overview.visible;
@ -1037,7 +1062,7 @@ MessageTray.prototype = {
Main.chrome.addActor(this.actor, { affectsStruts: false, Main.chrome.addActor(this.actor, { affectsStruts: false,
visibleInOverview: true }); visibleInOverview: true });
Main.chrome.trackActor(this._notificationBin); Main.chrome.trackActor(this._notificationBin);
Main.chrome.trackActor(this._summaryNotificationBoxPointer.actor); Main.chrome.trackActor(this._summaryBoxPointer.actor);
global.gdk_screen.connect('monitors-changed', Lang.bind(this, this._setSizePosition)); global.gdk_screen.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
@ -1138,9 +1163,9 @@ MessageTray.prototype = {
this._onSummaryItemHoverChanged(summaryItem); this._onSummaryItemHoverChanged(summaryItem);
})); }));
summaryItem.actor.connect('clicked', Lang.bind(this, summaryItem.actor.connect('button-press-event', Lang.bind(this,
function () { function (actor, event) {
this._onSummaryItemClicked(summaryItem); this._onSummaryItemClicked(summaryItem, event);
})); }));
source.connect('destroy', Lang.bind(this, this._onSourceDestroy)); source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
@ -1354,11 +1379,16 @@ MessageTray.prototype = {
this._expandedSummaryItem.setEllipsization(Pango.EllipsizeMode.END); this._expandedSummaryItem.setEllipsization(Pango.EllipsizeMode.END);
}, },
_onSummaryItemClicked: function(summaryItem) { _onSummaryItemClicked: function(summaryItem, event) {
if (!this._clickedSummaryItem || this._clickedSummaryItem != summaryItem) let clickedButton = event.get_button();
if (!this._clickedSummaryItem ||
this._clickedSummaryItem != summaryItem ||
this._clickedSummaryItemMouseButton != clickedButton) {
this._clickedSummaryItem = summaryItem; this._clickedSummaryItem = summaryItem;
else this._clickedSummaryItemMouseButton = clickedButton;
} else {
this._unsetClickedSummaryItem(); this._unsetClickedSummaryItem();
}
this._updateState(); this._updateState();
}, },
@ -1385,7 +1415,7 @@ MessageTray.prototype = {
// leaving the tray. The tray is locked when the summary notification is visible anyway, but we // leaving the tray. The tray is locked when the summary notification is visible anyway, but we
// should treat the mouse being over the summary notification as the tray being left for collapsing // should treat the mouse being over the summary notification as the tray being left for collapsing
// any expanded summary item other than the one related to the notification. // any expanded summary item other than the one related to the notification.
if (this._summaryNotificationBoxPointer.bin.hover) if (this._summaryBoxPointer.bin.hover)
return; return;
this._useLongerTrayLeftTimeout = false; this._useLongerTrayLeftTimeout = false;
@ -1543,19 +1573,23 @@ MessageTray.prototype = {
} }
// Summary notification // Summary notification
let haveSummaryNotification = this._clickedSummaryItem != null; let haveClickedSummaryItem = this._clickedSummaryItem != null;
let summaryNotificationIsMainNotification = (haveSummaryNotification && let summaryNotificationIsMainNotification = (haveClickedSummaryItem &&
this._clickedSummaryItem.source.notification == this._notification); this._clickedSummaryItem.source.notification == this._notification);
let canShowSummaryNotification = this._summaryState == State.SHOWN; let canShowSummaryBoxPointer = this._summaryState == State.SHOWN;
let wrongSummaryNotification = (haveSummaryNotification && let wrongSummaryNotification = (this._clickedSummaryItemMouseButton == 1 &&
this._summaryNotification != this._clickedSummaryItem.source.notification); this._summaryNotification != this._clickedSummaryItem.source.notification);
let wrongSummaryRightClickMenu = (this._clickedSummaryItemMouseButton == 3 &&
this._summaryBoxPointer.bin.child != this._clickedSummaryItem.rightClickMenu);
let wrongSummaryBoxPointer = (haveClickedSummaryItem &&
(wrongSummaryNotification || wrongSummaryRightClickMenu));
if (this._summaryNotificationState == State.HIDDEN) { if (this._summaryBoxPointerState == State.HIDDEN) {
if (haveSummaryNotification && !summaryNotificationIsMainNotification && canShowSummaryNotification) if (haveClickedSummaryItem && !summaryNotificationIsMainNotification && canShowSummaryBoxPointer)
this._showSummaryNotification(); this._showSummaryBoxPointer();
} else if (this._summaryNotificationState == State.SHOWN) { } else if (this._summaryBoxPointerState == State.SHOWN) {
if (!haveSummaryNotification || !canShowSummaryNotification || wrongSummaryNotification) if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer)
this._hideSummaryNotification(); this._hideSummaryBoxPointer();
} }
// Tray itself // Tray itself
@ -1813,44 +1847,53 @@ MessageTray.prototype = {
this._expandedSummaryItemTitleWidth = this._summaryItemTitleWidth; this._expandedSummaryItemTitleWidth = this._summaryItemTitleWidth;
}, },
_showSummaryNotification: function() { _showSummaryBoxPointer: function() {
this._summaryNotification = this._clickedSummaryItem.source.notification; if (this._clickedSummaryItemMouseButton == 1) {
this._summaryNotificationClickedId = this._summaryNotification.connect('done-displaying', let clickedSummaryItemNotification = this._clickedSummaryItem.source.notification;
Lang.bind(this, this._escapeTray)); let index = this._notificationQueue.indexOf(clickedSummaryItemNotification);
let index = this._notificationQueue.indexOf(this._summaryNotification);
if (index != -1) if (index != -1)
this._notificationQueue.splice(index, 1); this._notificationQueue.splice(index, 1);
this._summaryNotificationBoxPointer.bin.child = this._summaryNotification.actor; this._summaryNotification = clickedSummaryItemNotification;
this._focusGrabber.grabFocus(this._summaryNotification.actor); this._summaryNotificationClickedId = this._summaryNotification.connect('done-displaying',
Lang.bind(this, this._escapeTray));
this._summaryBoxPointer.bin.child = this._summaryNotification.actor;
if (!this._summaryNotificationExpandedId) if (!this._summaryNotificationExpandedId)
this._summaryNotificationExpandedId = this._summaryNotification.connect('expanded', Lang.bind(this, this._onSummaryNotificationExpanded)); this._summaryNotificationExpandedId = this._summaryNotification.connect('expanded',
Lang.bind(this, this._onSummaryBoxPointerExpanded));
this._summaryNotification.expand(false); this._summaryNotification.expand(false);
} else if (this._clickedSummaryItemMouseButton == 3) {
this._summaryRightClickMenuClickedId = this._clickedSummaryItem.connect('right-click-menu-done-displaying',
Lang.bind(this, this._escapeTray));
this._summaryBoxPointer.bin.child = this._clickedSummaryItem.rightClickMenu;
}
this._focusGrabber.grabFocus(this._summaryBoxPointer.bin.child);
this._clickedSummaryItemAllocationChangedId = this._clickedSummaryItemAllocationChangedId =
this._clickedSummaryItem.actor.connect('allocation-changed', this._clickedSummaryItem.actor.connect('allocation-changed',
Lang.bind(this, this._adjustNotificationBoxPointerPosition)); Lang.bind(this, this._adjustSummaryBoxPointerPosition));
// _clickedSummaryItem.actor can change absolute postiion without changing allocation // _clickedSummaryItem.actor can change absolute postiion without changing allocation
this._summaryMotionId = this._summary.connect('allocation-changed', this._summaryMotionId = this._summary.connect('allocation-changed',
Lang.bind(this, this._adjustNotificationBoxPointerPosition)); Lang.bind(this, this._adjustSummaryBoxPointerPosition));
this._summaryNotificationBoxPointer.actor.opacity = 0; this._summaryBoxPointer.actor.opacity = 0;
this._summaryNotificationBoxPointer.actor.show(); this._summaryBoxPointer.actor.show();
this._adjustNotificationBoxPointerPosition(); this._adjustSummaryBoxPointerPosition();
this._summaryNotificationState = State.SHOWING; this._summaryBoxPointerState = State.SHOWING;
this._summaryNotificationBoxPointer.show(true, Lang.bind(this, function() { this._summaryBoxPointer.show(true, Lang.bind(this, function() {
this._summaryNotificationState = State.SHOWN; this._summaryBoxPointerState = State.SHOWN;
})); }));
}, },
_adjustNotificationBoxPointerPosition: function() { _adjustSummaryBoxPointerPosition: function() {
// The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor // The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor
if (!this._clickedSummaryItem) if (!this._clickedSummaryItem)
return; return;
this._summaryNotificationBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5); this._summaryBoxPointer.setPosition(this._clickedSummaryItem.actor, 0, 0.5);
}, },
_unsetClickedSummaryItem: function() { _unsetClickedSummaryItem: function() {
@ -1861,14 +1904,20 @@ MessageTray.prototype = {
this._summaryMotionId = 0; this._summaryMotionId = 0;
} }
if (this._summaryRightClickMenuClickedId) {
this._clickedSummaryItem.disconnect(this._summaryRightClickMenuClickedId);
this._summaryRightClickMenuClickedId = 0;
}
this._clickedSummaryItem = null; this._clickedSummaryItem = null;
this._clickedSummaryItemMouseButton = -1;
}, },
_onSummaryNotificationExpanded: function() { _onSummaryBoxPointerExpanded: function() {
this._adjustNotificationBoxPointerPosition(); this._adjustSummaryBoxPointerPosition();
}, },
_hideSummaryNotification: function() { _hideSummaryBoxPointer: function() {
if (this._summaryNotificationExpandedId) { if (this._summaryNotificationExpandedId) {
this._summaryNotification.disconnect(this._summaryNotificationExpandedId); this._summaryNotification.disconnect(this._summaryNotificationExpandedId);
this._summaryNotificationExpandedId = 0; this._summaryNotificationExpandedId = 0;
@ -1878,13 +1927,14 @@ MessageTray.prototype = {
this._unsetClickedSummaryItem(); this._unsetClickedSummaryItem();
this._focusGrabber.ungrabFocus(); this._focusGrabber.ungrabFocus();
this._summaryNotificationState = State.HIDING; this._summaryBoxPointerState = State.HIDING;
this._summaryNotificationBoxPointer.hide(true, Lang.bind(this, this._hideSummaryNotificationCompleted)); this._summaryBoxPointer.hide(true, Lang.bind(this, this._hideSummaryBoxPointerCompleted));
}, },
_hideSummaryNotificationCompleted: function() { _hideSummaryBoxPointerCompleted: function() {
this._summaryNotificationState = State.HIDDEN; this._summaryBoxPointerState = State.HIDDEN;
this._summaryNotificationBoxPointer.bin.child = null; this._summaryBoxPointer.bin.child = null;
if (this._summaryNotification != null) {
this._summaryNotification.collapseCompleted(); this._summaryNotification.collapseCompleted();
this._summaryNotification.disconnect(this._summaryNotificationClickedId); this._summaryNotification.disconnect(this._summaryNotificationClickedId);
this._summaryNotificationClickedId = 0; this._summaryNotificationClickedId = 0;
@ -1896,6 +1946,7 @@ MessageTray.prototype = {
this._onNotify(summaryNotification.source, summaryNotification); this._onNotify(summaryNotification.source, summaryNotification);
this._reNotifyWithSummaryNotificationAfterHide = false; this._reNotifyWithSummaryNotificationAfterHide = false;
} }
}
if (this._clickedSummaryItem) if (this._clickedSummaryItem)
this._updateState(); this._updateState();
} }
@ -1920,7 +1971,7 @@ SystemNotificationSource.prototype = {
icon_size: this.ICON_SIZE }); icon_size: this.ICON_SIZE });
}, },
_notificationClicked: function() { open: function() {
this.destroy(); this.destroy();
} }
}; };

View File

@ -473,7 +473,7 @@ Source.prototype = {
this._isTrayIcon = true; this._isTrayIcon = true;
}, },
_notificationClicked: function(notification) { open: function(notification) {
this.openApp(); this.openApp();
}, },

View File

@ -54,8 +54,10 @@ PopupBaseMenuItem.prototype = {
} }
if (params.reactive && params.hover) if (params.reactive && params.hover)
this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged)); this.actor.connect('notify::hover', Lang.bind(this, this._onHoverChanged));
if (params.reactive) if (params.reactive) {
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn)); this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
}
}, },
_onStyleChanged: function (actor) { _onStyleChanged: function (actor) {
@ -81,6 +83,10 @@ PopupBaseMenuItem.prototype = {
this.setActive(true); this.setActive(true);
}, },
_onKeyFocusOut: function (actor) {
this.setActive(false);
},
_onHoverChanged: function (actor) { _onHoverChanged: function (actor) {
this.setActive(actor.hover); this.setActive(actor.hover);
}, },

View File

@ -173,7 +173,7 @@ Source.prototype = {
} }
}, },
_notificationClicked: function(notification) { open: function(notification) {
let props = {}; let props = {};
props[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT; props[Tp.PROP_CHANNEL_CHANNEL_TYPE] = Tp.IFACE_CHANNEL_TYPE_TEXT;
[props[Tp.PROP_CHANNEL_TARGET_HANDLE], props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE]] = this._channel.get_handle(); [props[Tp.PROP_CHANNEL_TARGET_HANDLE], props[Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE]] = this._channel.get_handle();

View File

@ -101,7 +101,7 @@ Source.prototype = {
return this._app.create_icon_texture(this.ICON_SIZE); return this._app.create_icon_texture(this.ICON_SIZE);
}, },
_notificationClicked : function(notification) { open : function(notification) {
Main.activateWindow(this._window); Main.activateWindow(this._window);
this.destroy(); this.destroy();
} }