kill tray 2

This commit is contained in:
Jasper St. Pierre 2014-06-13 11:58:40 -04:00
parent a80b8f7791
commit 2b93bcf921
2 changed files with 3 additions and 525 deletions

View File

@ -95,64 +95,6 @@ function _fixMarkup(text, allowMarkup) {
return GLib.markup_escape_text(text, -1);
}
const FocusGrabber = new Lang.Class({
Name: 'FocusGrabber',
_init: function(actor) {
this._actor = actor;
this._prevKeyFocusActor = null;
this._focusActorChangedId = 0;
this._focused = false;
},
grabFocus: function() {
if (this._focused)
return;
this._prevKeyFocusActor = global.stage.get_key_focus();
this._focusActorChangedId = global.stage.connect('notify::key-focus', Lang.bind(this, this._focusActorChanged));
if (!this._actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
this._actor.grab_key_focus();
this._focused = true;
},
_focusUngrabbed: function() {
if (!this._focused)
return false;
if (this._focusActorChangedId > 0) {
global.stage.disconnect(this._focusActorChangedId);
this._focusActorChangedId = 0;
}
this._focused = false;
return true;
},
_focusActorChanged: function() {
let focusedActor = global.stage.get_key_focus();
if (!focusedActor || !this._actor.contains(focusedActor))
this._focusUngrabbed();
},
ungrabFocus: function() {
if (!this._focusUngrabbed())
return;
if (this._prevKeyFocusActor) {
global.stage.set_key_focus(this._prevKeyFocusActor);
this._prevKeyFocusActor = null;
} else {
let focusedActor = global.stage.get_key_focus();
if (focusedActor && this._actor.contains(focusedActor))
global.stage.set_key_focus(null);
}
}
});
const URLHighlighter = new Lang.Class({
Name: 'URLHighlighter',
@ -980,79 +922,9 @@ const MessageTray = new Lang.Class({
Name: 'MessageTray',
_init: function() {
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
this._onStatusChanged(proxy.status);
}));
this._busy = false;
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._onStatusChanged(status);
}));
this._notificationWidget = new St.Widget({ name: 'notification-container',
reactive: true,
track_hover: true,
y_align: Clutter.ActorAlign.START,
x_align: Clutter.ActorAlign.CENTER,
y_expand: true,
x_expand: true,
layout_manager: new Clutter.BinLayout() });
this._notificationWidget.connect('key-release-event', Lang.bind(this, this._onNotificationKeyRelease));
this._notificationWidget.connect('notify::hover', Lang.bind(this, this._onNotificationHoverChanged));
this._notificationWidget.connect('notify::height', Lang.bind(this, function() {
this._notificationWidget.translation_y = -this._notificationWidget.height;
}));
this._notificationBin = new St.Bin({ y_expand: true });
this._notificationBin.set_y_align(Clutter.ActorAlign.START);
this._notificationWidget.add_actor(this._notificationBin);
this._notificationWidget.hide();
this._notificationFocusGrabber = new FocusGrabber(this._notificationWidget);
this._notificationQueue = [];
this._notification = null;
this._notificationClickedId = 0;
this._userActiveWhileNotificationShown = false;
this.idleMonitor = Meta.IdleMonitor.get_core();
// pointerInNotification is sort of a misnomer -- it tracks whether
// a message tray notification should expand. The value is
// partially driven by the hover state of the notification, but has
// a lot of complex state related to timeouts and the current
// state of the pointer when a notification pops up.
this._pointerInNotification = false;
// This tracks this._notificationWidget.hover and is used to fizzle
// out non-changing hover notifications in onNotificationHoverChanged.
this._notificationHovered = false;
this._keyboardVisible = false;
this._notificationState = State.HIDDEN;
this._notificationTimeoutId = 0;
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
this._inCtrlAltTab = false;
Main.layoutManager.trackChrome(this._notificationWidget);
global.screen.connect('in-fullscreen-changed', Lang.bind(this, this._updateState));
this._sources = new Map();
},
close: function() {
this._escapeTray();
},
_onNotificationKeyRelease: function(actor, event) {
if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
this._expireNotification();
return Clutter.EVENT_STOP;
}
return Clutter.EVENT_PROPAGATE;
},
_expireNotification: function() {
this._notificationExpired = true;
this._updateState();
@ -1072,7 +944,7 @@ const MessageTray = new Lang.Class({
source.policy.store();
source.policy.connect('enable-changed', Lang.bind(this, this._onSourceEnableChanged, source));
source.policy.connect('policy-changed', Lang.bind(this, this._updateState));
// source.policy.connect('policy-changed', Lang.bind(this, this._updateState));
this._onSourceEnableChanged(source.policy, source);
},
@ -1130,402 +1002,8 @@ const MessageTray = new Lang.Class({
this._removeSource(source);
},
_onNotificationDestroy: function(notification) {
if (this._notification == notification && (this._notificationState == State.SHOWN || this._notificationState == State.SHOWING)) {
this._updateNotificationTimeout(0);
this._notificationRemoved = true;
this._updateState();
return;
}
let index = this._notificationQueue.indexOf(notification);
if (index != -1)
this._notificationQueue.splice(index, 1);
},
openTray: function() {
if (Main.overview.animationInProgress)
return;
this._traySummoned = true;
this._updateState();
},
hide: function() {
this._traySummoned = false;
this._updateState();
},
_onNotify: function(source, notification) {
if (this._notification == notification) {
// If a notification that is being shown is updated, we update
// how it is shown and extend the time until it auto-hides.
// If a new notification is updated while it is being hidden,
// we stop hiding it and show it again.
this._updateShowingNotification();
} else if (this._notificationQueue.indexOf(notification) < 0) {
notification.connect('destroy',
Lang.bind(this, this._onNotificationDestroy));
this._notificationQueue.push(notification);
this._notificationQueue.sort(function(notification1, notification2) {
return (notification2.urgency - notification1.urgency);
});
}
this._updateState();
},
_resetNotificationLeftTimeout: function() {
this._useLongerNotificationLeftTimeout = false;
if (this._notificationLeftTimeoutId) {
Mainloop.source_remove(this._notificationLeftTimeoutId);
this._notificationLeftTimeoutId = 0;
this._notificationLeftMouseX = -1;
this._notificationLeftMouseY = -1;
}
},
_onNotificationHoverChanged: function() {
if (this._notificationWidget.hover == this._notificationHovered)
return;
this._notificationHovered = this._notificationWidget.hover;
if (this._notificationHovered) {
// No dwell inside notifications at the bottom of the screen
// this._cancelTrayDwell();
this._resetNotificationLeftTimeout();
if (this._showNotificationMouseX >= 0) {
let actorAtShowNotificationPosition =
global.stage.get_actor_at_pos(Clutter.PickMode.ALL, this._showNotificationMouseX, this._showNotificationMouseY);
this._showNotificationMouseX = -1;
this._showNotificationMouseY = -1;
// Don't set this._pointerInNotification to true if the pointer was initially in the area where the notification
// popped up. That way we will not be expanding notifications that happen to pop up over the pointer
// automatically. Instead, the user is able to expand the notification by mousing away from it and then
// mousing back in. Because this is an expected action, we set the boolean flag that indicates that a longer
// timeout should be used before popping down the notification.
/*
if (this.actor.contains(actorAtShowNotificationPosition)) {
this._useLongerNotificationLeftTimeout = true;
return;
}
*/
}
this._pointerInNotification = true;
this._updateState();
} else {
// We record the position of the mouse the moment it leaves the tray. These coordinates are used in
// this._onNotificationLeftTimeout() to determine if the mouse has moved far enough during the initial timeout for us
// to consider that the user intended to leave the tray and therefore hide the tray. If the mouse is still
// close to its previous position, we extend the timeout once.
let [x, y, mods] = global.get_pointer();
this._notificationLeftMouseX = x;
this._notificationLeftMouseY = y;
// We wait just a little before hiding the message tray in case the user quickly moves the mouse back into it.
// We wait for a longer period if the notification popped up where the mouse pointer was already positioned.
// That gives the user more time to mouse away from the notification and mouse back in in order to expand it.
let timeout = this._useLongerNotificationLeftTimeout ? LONGER_HIDE_TIMEOUT * 1000 : HIDE_TIMEOUT * 1000;
this._notificationLeftTimeoutId = Mainloop.timeout_add(timeout, Lang.bind(this, this._onNotificationLeftTimeout));
GLib.Source.set_name_by_id(this._notificationLeftTimeoutId, '[gnome-shell] this._onNotificationLeftTimeout');
}
},
_onKeyboardVisibleChanged: function(layoutManager, keyboardVisible) {
this._keyboardVisible = keyboardVisible;
this._updateState();
},
_onStatusChanged: function(status) {
if (status == GnomeSession.PresenceStatus.BUSY) {
// remove notification and allow the summary to be closed now
this._updateNotificationTimeout(0);
this._busy = true;
} else if (status != GnomeSession.PresenceStatus.IDLE) {
// We preserve the previous value of this._busy if the status turns to IDLE
// so that we don't start showing notifications queued during the BUSY state
// as the screensaver gets activated.
this._busy = false;
}
this._updateState();
},
_onNotificationLeftTimeout: function() {
let [x, y, mods] = global.get_pointer();
// We extend the timeout once if the mouse moved no further than MOUSE_LEFT_ACTOR_THRESHOLD to either side or up.
// We don't check how far down the mouse moved because any point above the tray, but below the exit coordinate,
// is close to the tray.
if (this._notificationLeftMouseX > -1 &&
y > this._notificationLeftMouseY - MOUSE_LEFT_ACTOR_THRESHOLD &&
x < this._notificationLeftMouseX + MOUSE_LEFT_ACTOR_THRESHOLD &&
x > this._notificationLeftMouseX - MOUSE_LEFT_ACTOR_THRESHOLD) {
this._notificationLeftMouseX = -1;
this._notificationLeftTimeoutId = Mainloop.timeout_add(LONGER_HIDE_TIMEOUT * 1000,
Lang.bind(this, this._onNotificationLeftTimeout));
GLib.Source.set_name_by_id(this._notificationLeftTimeoutId, '[gnome-shell] this._onNotificationLeftTimeout');
} else {
this._notificationLeftTimeoutId = 0;
this._useLongerNotificationLeftTimeout = false;
this._pointerInNotification = false;
this._updateNotificationTimeout(0);
this._updateState();
}
return GLib.SOURCE_REMOVE;
},
_escapeTray: function() {
this._pointerInNotification = false;
this._traySummoned = false;
this._updateNotificationTimeout(0);
this._updateState();
},
// All of the logic for what happens when occurs here; the various
// event handlers merely update variables such as
// 'this._pointerInNotification', 'this._traySummoned', etc, and
// _updateState() figures out what (if anything) needs to be done
// at the present time.
_updateState: function() {
// If our state changes caused _updateState to be called,
// just exit now to prevent reentrancy issues.
if (this._updatingState)
return;
this._updatingState = true;
// Filter out acknowledged notifications.
this._notificationQueue = this._notificationQueue.filter(function(n) {
return !n.acknowledged;
});
let hasNotifications = Main.sessionMode.hasNotifications;
if (this._notificationState == State.HIDDEN) {
let shouldShowNotification = (hasNotifications && !this._traySummoned);
let nextNotification = this._notificationQueue[0] || null;
if (shouldShowNotification && nextNotification) {
let limited = this._busy || Main.layoutManager.bottomMonitor.inFullscreen;
let showNextNotification = (!limited || nextNotification.forFeedback || nextNotification.urgency == Urgency.CRITICAL);
if (showNextNotification)
this._showNotification();
}
} else if (this._notificationState == State.SHOWN) {
let expired = (this._userActiveWhileNotificationShown &&
this._notificationTimeoutId == 0 &&
this._notification.urgency != Urgency.CRITICAL &&
!this._notification.focused &&
!this._pointerInNotification) || this._notificationExpired;
let mustClose = (this._notificationRemoved || !hasNotifications || expired || this._traySummoned);
if (mustClose) {
let animate = hasNotifications && !this._notificationRemoved;
this._hideNotification(animate);
} else if (this._pointerInNotification) {
this._ensureNotificationFocused();
}
}
this._updatingState = false;
// Clean transient variables that are used to communicate actions
// to updateState()
this._notificationExpired = false;
},
_tween: function(actor, statevar, value, params) {
let onComplete = params.onComplete;
let onCompleteScope = params.onCompleteScope;
let onCompleteParams = params.onCompleteParams;
params.onComplete = this._tweenComplete;
params.onCompleteScope = this;
params.onCompleteParams = [statevar, value, onComplete, onCompleteScope, onCompleteParams];
// Remove other tweens that could mess with the state machine
Tweener.removeTweens(actor);
Tweener.addTween(actor, params);
let valuing = (value == State.SHOWN) ? State.SHOWING : State.HIDING;
this[statevar] = valuing;
},
_tweenComplete: function(statevar, value, onComplete, onCompleteScope, onCompleteParams) {
this[statevar] = value;
if (onComplete)
onComplete.apply(onCompleteScope, onCompleteParams);
this._updateState();
},
_showTray: function() {
/*
if (!this._grabHelper.grab({ actor: this.actor,
onUngrab: Lang.bind(this, this._escapeTray) })) {
this._traySummoned = false;
return false;
}
this.emit('showing');
this._tween(this.actor, '_trayState', State.SHOWN,
{ y: -this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
*/
return true;
},
_onIdleMonitorBecameActive: function() {
this._userActiveWhileNotificationShown = true;
this._updateNotificationTimeout(2000);
this._updateState();
},
_showNotification: function() {
this._notification = this._notificationQueue.shift();
this._userActiveWhileNotificationShown = this.idleMonitor.get_idletime() <= IDLE_TIME;
if (!this._userActiveWhileNotificationShown) {
// If the user isn't active, set up a watch to let us know
// when the user becomes active.
this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive));
}
this._notificationClickedId = this._notification.connect('done-displaying',
Lang.bind(this, this._escapeTray));
this._notificationUnfocusedId = this._notification.connect('unfocused', Lang.bind(this, function() {
this._updateState();
}));
this._notificationBin.child = this._notification.actor;
this._notificationWidget.opacity = 0;
this._notificationWidget.show();
this._updateShowingNotification();
let [x, y, mods] = global.get_pointer();
// We save the position of the mouse at the time when we started showing the notification
// in order to determine if the notification popped up under it. We make that check if
// the user starts moving the mouse and _onNotificationHoverChanged() gets called. We don't
// expand the notification if it just happened to pop up under the mouse unless the user
// explicitly mouses away from it and then mouses back in.
this._showNotificationMouseX = x;
this._showNotificationMouseY = y;
// We save the coordinates of the mouse at the time when we started showing the notification
// and then we update it in _notificationTimeout(). We don't pop down the notification if
// the mouse is moving towards it or within it.
this._lastSeenMouseX = x;
this._lastSeenMouseY = y;
this._resetNotificationLeftTimeout();
},
_updateShowingNotification: function() {
this._notification.acknowledged = true;
this._notification.playSound();
// We tween all notifications to full opacity. This ensures that both new notifications and
// notifications that might have been in the process of hiding get full opacity.
//
// We use this._showNotificationCompleted() onComplete callback to extend the time the updated
// notification is being shown.
this._tween(this._notificationWidget, '_notificationState', State.SHOWN,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._showNotificationCompleted,
onCompleteScope: this
});
},
_showNotificationCompleted: function() {
if (this._notification.urgency != Urgency.CRITICAL)
this._updateNotificationTimeout(NOTIFICATION_TIMEOUT * 1000);
},
_updateNotificationTimeout: function(timeout) {
if (this._notificationTimeoutId) {
Mainloop.source_remove(this._notificationTimeoutId);
this._notificationTimeoutId = 0;
}
if (timeout > 0) {
this._notificationTimeoutId =
Mainloop.timeout_add(timeout,
Lang.bind(this, this._notificationTimeout));
GLib.Source.set_name_by_id(this._notificationTimeoutId, '[gnome-shell] this._notificationTimeout');
}
},
_notificationTimeout: function() {
let [x, y, mods] = global.get_pointer();
if (y > this._lastSeenMouseY + 10 && !this._notificationHovered) {
// The mouse is moving towards the notification, so don't
// hide it yet. (We just create a new timeout (and destroy
// the old one) each time because the bookkeeping is
// simpler.)
this._updateNotificationTimeout(1000);
} else if (this._useLongerNotificationLeftTimeout && !this._notificationLeftTimeoutId &&
(x != this._lastSeenMouseX || y != this._lastSeenMouseY)) {
// Refresh the timeout if the notification originally
// popped up under the pointer, and the pointer is hovering
// inside it.
this._updateNotificationTimeout(1000);
} else {
this._notificationTimeoutId = 0;
this._updateState();
}
this._lastSeenMouseX = x;
this._lastSeenMouseY = y;
return GLib.SOURCE_REMOVE;
},
_hideNotification: function(animate) {
this._notificationFocusGrabber.ungrabFocus();
if (this._notificationClickedId) {
this._notification.disconnect(this._notificationClickedId);
this._notificationClickedId = 0;
}
if (this._notificationUnfocusedId) {
this._notification.disconnect(this._notificationUnfocusedId);
this._notificationUnfocusedId = 0;
}
this._resetNotificationLeftTimeout();
if (animate) {
this._tween(this._notificationWidget, '_notificationState', State.HIDDEN,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._hideNotificationCompleted,
onCompleteScope: this
});
} else {
Tweener.removeTweens(this._notificationWidget);
this._notificationWidget.opacity = 0;
this._notificationState = State.HIDDEN;
this._hideNotificationCompleted();
}
},
_hideNotificationCompleted: function() {
let notification = this._notification;
this._notification = null;
this._pointerInNotification = false;
this._notificationRemoved = false;
this._notificationBin.child = null;
this._notificationWidget.hide();
},
_ensureNotificationFocused: function() {
this._notificationFocusGrabber.grabFocus();
// Fill in here.
},
});
Signals.addSignalMethods(MessageTray.prototype);

View File

@ -114,7 +114,7 @@ const FdoNotificationDaemon = new Lang.Class({
Main.overview.connect('hidden',
Lang.bind(this, this._onFocusAppChanged));
this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
// this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
},
_imageForNotificationData: function(hints) {