mirror of
https://github.com/edu4rdshl/gnome-shell.git
synced 2026-07-18 07:34:54 +00:00
Merge branch 'notification_drop_hover' into 'main'
Draft: messageTray: Remove expand on hover and simplify how notifications are dismissed See merge request GNOME/gnome-shell!3364
This commit is contained in:
commit
6615ed00cd
2 changed files with 147 additions and 328 deletions
|
|
@ -593,12 +593,14 @@ export const Message = GObject.registerClass({
|
|||
duration,
|
||||
});
|
||||
|
||||
this._actionBin.scale_y = 0;
|
||||
this._actionBin.ease({
|
||||
scale_y: 1,
|
||||
duration,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
});
|
||||
if (!this._alwaysShowButtons) {
|
||||
this._actionBin.scale_y = 0;
|
||||
this._actionBin.ease({
|
||||
scale_y: 1,
|
||||
duration,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
});
|
||||
}
|
||||
|
||||
this._header.expandButton.ease({
|
||||
rotation_angle_z: 180,
|
||||
|
|
@ -608,23 +610,33 @@ export const Message = GObject.registerClass({
|
|||
this.emit('expanded');
|
||||
}
|
||||
|
||||
set alwaysShowButtons(value) {
|
||||
this._actionBin.visible = this._actionBin.child && value;
|
||||
this._actionBin.scale_y = value ? 1 : 0;
|
||||
this._alwaysShowButtons = value;
|
||||
}
|
||||
|
||||
unexpand(animate) {
|
||||
const duration = animate ? MessageTray.ANIMATION_TIME : 0;
|
||||
this._bodyBin.ease_property('@layout.expansion', 0, {
|
||||
progress_mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
duration,
|
||||
});
|
||||
|
||||
this._actionBin.ease({
|
||||
scale_y: 0,
|
||||
duration,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: () => {
|
||||
this._actionBin.hide();
|
||||
this.expanded = false;
|
||||
},
|
||||
});
|
||||
|
||||
if (!this._alwaysShowButtons) {
|
||||
this._actionBin.ease({
|
||||
scale_y: 0,
|
||||
duration,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
onComplete: () => {
|
||||
this._actionBin.hide();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
this._header.expandButton.ease({
|
||||
rotation_angle_z: 0,
|
||||
duration,
|
||||
|
|
|
|||
|
|
@ -19,18 +19,11 @@ const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
|
|||
|
||||
export const ANIMATION_TIME = 200;
|
||||
|
||||
const NOTIFICATION_TIMEOUT = 4000;
|
||||
|
||||
const HIDE_TIMEOUT = 200;
|
||||
const LONGER_HIDE_TIMEOUT = 600;
|
||||
const NOTIFICATION_TIMEOUT = 1500;
|
||||
|
||||
const MAX_NOTIFICATIONS_IN_QUEUE = 3;
|
||||
const MAX_NOTIFICATIONS_PER_SOURCE = 3;
|
||||
|
||||
// We delay hiding of the tray if the mouse is within MOUSE_LEFT_ACTOR_THRESHOLD
|
||||
// range from the point where it left the tray.
|
||||
const MOUSE_LEFT_ACTOR_THRESHOLD = 20;
|
||||
|
||||
const IDLE_TIME = 1000;
|
||||
|
||||
export const State = {
|
||||
|
|
@ -702,7 +695,7 @@ export const MessageTray = GObject.registerClass({
|
|||
this._onStatusChanged(status);
|
||||
});
|
||||
|
||||
let constraint = new Layout.MonitorConstraint({primary: true});
|
||||
const constraint = new Layout.MonitorConstraint({primary: true});
|
||||
Main.layoutManager.panelBox.bind_property('visible',
|
||||
constraint, 'work-area',
|
||||
GObject.BindingFlags.SYNC_CREATE);
|
||||
|
|
@ -722,40 +715,23 @@ export const MessageTray = GObject.registerClass({
|
|||
this._onNotificationKeyRelease.bind(this));
|
||||
this._bannerBin.connect('notify::hover',
|
||||
this._onNotificationHoverChanged.bind(this));
|
||||
|
||||
this.add_child(this._bannerBin);
|
||||
|
||||
this._notificationFocusGrabber = new FocusGrabber(this._bannerBin);
|
||||
this._notificationQueue = [];
|
||||
this._notification = null;
|
||||
this._banner = null;
|
||||
|
||||
this._userActiveWhileNotificationShown = false;
|
||||
this._notificationTimeoutId = 0;
|
||||
|
||||
this.idleMonitor = global.backend.get_core_idle_monitor();
|
||||
|
||||
this._useLongerNotificationLeftTimeout = false;
|
||||
|
||||
// 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._bannerBin.hover and is used to fizzle
|
||||
// out non-changing hover notifications in onNotificationHoverChanged.
|
||||
this._notificationHovered = false;
|
||||
|
||||
this._notificationState = State.HIDDEN;
|
||||
this._notificationTimeoutId = 0;
|
||||
this._notificationRemoved = false;
|
||||
|
||||
Main.layoutManager.addChrome(this, {affectsInputRegion: false});
|
||||
Main.layoutManager.trackChrome(this._bannerBin, {affectsInputRegion: true});
|
||||
|
||||
global.display.connect('in-fullscreen-changed', this._updateState.bind(this));
|
||||
global.display.connect('in-fullscreen-changed', this._updateShowBanners.bind(this));
|
||||
|
||||
Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
|
||||
Main.sessionMode.connect('updated', this._updateShowBanners.bind(this));
|
||||
|
||||
Main.overview.connect('window-drag-begin',
|
||||
this._onDragBegin.bind(this));
|
||||
|
|
@ -780,15 +756,11 @@ export const MessageTray = GObject.registerClass({
|
|||
new Gio.Settings({schema_id: SHELL_KEYBINDINGS_SCHEMA}),
|
||||
Meta.KeyBindingFlags.NONE,
|
||||
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
|
||||
this._expandActiveNotification.bind(this));
|
||||
() => this._notificationFocusGrabber.grabFocus());
|
||||
|
||||
this._sources = new Set();
|
||||
|
||||
this._sessionUpdated();
|
||||
}
|
||||
|
||||
_sessionUpdated() {
|
||||
this._updateState();
|
||||
this._updateShowBanners();
|
||||
}
|
||||
|
||||
_onDragBegin() {
|
||||
|
|
@ -809,18 +781,14 @@ export const MessageTray = GObject.registerClass({
|
|||
|
||||
_onNotificationKeyRelease(actor, event) {
|
||||
if (event.get_key_symbol() === Clutter.KEY_Escape && event.get_state() === 0) {
|
||||
this._expireNotification();
|
||||
// TODO: this should be handled by the NotificationMessage itself not here
|
||||
this._notification?.destroy();
|
||||
return Clutter.EVENT_STOP;
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
_expireNotification() {
|
||||
this._notificationExpired = true;
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
get queueCount() {
|
||||
return this._notificationQueue.length;
|
||||
}
|
||||
|
|
@ -829,7 +797,7 @@ export const MessageTray = GObject.registerClass({
|
|||
if (this._bannerBlocked === v)
|
||||
return;
|
||||
this._bannerBlocked = v;
|
||||
this._updateState();
|
||||
this._updateShowBanners();
|
||||
}
|
||||
|
||||
contains(source) {
|
||||
|
|
@ -848,7 +816,6 @@ export const MessageTray = GObject.registerClass({
|
|||
source.policy.connect('notify::enable', () => {
|
||||
this._onSourceEnableChanged(source.policy, source);
|
||||
});
|
||||
source.policy.connect('notify', this._updateState.bind(this));
|
||||
this._onSourceEnableChanged(source.policy, source);
|
||||
}
|
||||
|
||||
|
|
@ -874,8 +841,8 @@ export const MessageTray = GObject.registerClass({
|
|||
}
|
||||
|
||||
_onSourceEnableChanged(policy, source) {
|
||||
let wasEnabled = this.contains(source);
|
||||
let shouldBeEnabled = policy.enable;
|
||||
const wasEnabled = this.contains(source);
|
||||
const shouldBeEnabled = policy.enable;
|
||||
|
||||
if (wasEnabled !== shouldBeEnabled) {
|
||||
if (shouldBeEnabled)
|
||||
|
|
@ -885,15 +852,9 @@ export const MessageTray = GObject.registerClass({
|
|||
}
|
||||
}
|
||||
|
||||
_onNotificationRemoved(source, notification) {
|
||||
_onNotificationRemoved(source_, notification) {
|
||||
if (this._notification === notification) {
|
||||
this._notificationRemoved = true;
|
||||
if (this._notificationState === State.SHOWN ||
|
||||
this._notificationState === State.SHOWING) {
|
||||
this._pointerInNotification = false;
|
||||
this._updateNotificationTimeout(0);
|
||||
this._updateState();
|
||||
}
|
||||
this.showNextNotification();
|
||||
} else {
|
||||
const index = this._notificationQueue.indexOf(notification);
|
||||
if (index !== -1) {
|
||||
|
|
@ -919,11 +880,10 @@ export const MessageTray = GObject.registerClass({
|
|||
// 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();
|
||||
this._showNotification(notification);
|
||||
} else if (!this._notificationQueue.includes(notification)) {
|
||||
// If the queue is "full", we skip banner mode and just show a small
|
||||
// indicator in the panel; however do make an exception for CRITICAL
|
||||
// notifications, as only banner mode allows expansion.
|
||||
// indicator in the panel
|
||||
let bannerCount = this._notification ? 1 : 0;
|
||||
let full = this.queueCount + bannerCount >= MAX_NOTIFICATIONS_IN_QUEUE;
|
||||
if (!full || notification.urgency === Urgency.CRITICAL) {
|
||||
|
|
@ -933,224 +893,109 @@ export const MessageTray = GObject.registerClass({
|
|||
this.emit('queue-changed');
|
||||
}
|
||||
}
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
_resetNotificationLeftTimeout() {
|
||||
this._useLongerNotificationLeftTimeout = false;
|
||||
if (this._notificationLeftTimeoutId) {
|
||||
GLib.source_remove(this._notificationLeftTimeoutId);
|
||||
this._notificationLeftTimeoutId = 0;
|
||||
this._notificationLeftMouseX = -1;
|
||||
this._notificationLeftMouseY = -1;
|
||||
}
|
||||
this._updateShowBanners();
|
||||
}
|
||||
|
||||
_onNotificationHoverChanged() {
|
||||
if (this._bannerBin.hover === this._notificationHovered)
|
||||
if (!this._notification)
|
||||
return;
|
||||
|
||||
this._notificationHovered = this._bannerBin.hover;
|
||||
if (this._notificationHovered) {
|
||||
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._bannerBin.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] = 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 : HIDE_TIMEOUT;
|
||||
this._notificationLeftTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeout, this._onNotificationLeftTimeout.bind(this));
|
||||
GLib.Source.set_name_by_id(this._notificationLeftTimeoutId, '[gnome-shell] this._onNotificationLeftTimeout');
|
||||
}
|
||||
// TODO: the banner should take focus if the mouse moves into the banner but not when the banner popped up under the notification
|
||||
if (this._bannerBin.hover)
|
||||
this._clearNotificationTimeout();
|
||||
else
|
||||
this._showNextNotification();
|
||||
}
|
||||
|
||||
_onStatusChanged(status) {
|
||||
if (status === GnomeSession.PresenceStatus.BUSY) {
|
||||
// remove notification and allow the summary to be closed now
|
||||
this._updateNotificationTimeout(0);
|
||||
this._busy = true;
|
||||
// Hide current notification and go to the next notification
|
||||
// if there is a notification we want to show even when busy
|
||||
this._showNextNotification();
|
||||
} 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() {
|
||||
let [x, y] = global.get_pointer();
|
||||
// We extend the timeout once if the mouse moved no further than MOUSE_LEFT_ACTOR_THRESHOLD to either side.
|
||||
if (this._notificationLeftMouseX > -1 &&
|
||||
y < this._notificationLeftMouseY + MOUSE_LEFT_ACTOR_THRESHOLD &&
|
||||
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 = GLib.timeout_add(
|
||||
GLib.PRIORITY_DEFAULT,
|
||||
LONGER_HIDE_TIMEOUT,
|
||||
this._onNotificationLeftTimeout.bind(this));
|
||||
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();
|
||||
_updateShowBanners() {
|
||||
if (this._bannerBlocked || !Main.layoutManager.primaryMonitor || !Main.sessionMode.hasNotifications) {
|
||||
if (this._notification)
|
||||
this._hideNotification(this._notification);
|
||||
} else if (!this._notification) {
|
||||
this._showNextNotification();
|
||||
}
|
||||
return GLib.SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
// 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() {
|
||||
let hasMonitor = Main.layoutManager.primaryMonitor != null;
|
||||
this.visible = !this._bannerBlocked && hasMonitor && this._banner != null;
|
||||
if (this._bannerBlocked || !hasMonitor)
|
||||
return;
|
||||
_showNextNotification() {
|
||||
const busy = this._busy || Main.layoutManager.primaryMonitor.inFullscreen;
|
||||
const prevQueueLength = this._notificationQueue.length;
|
||||
|
||||
// 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.
|
||||
let changed = false;
|
||||
// Filter out acknowledged notification and notification's policy doesn't allow banners.
|
||||
this._notificationQueue = this._notificationQueue.filter(n => {
|
||||
changed ||= n.acknowledged;
|
||||
return !n.acknowledged;
|
||||
return !n.acknowledged && n.source.policy.showBanners;
|
||||
});
|
||||
|
||||
if (changed)
|
||||
const nextIndex = this._notificationQueue.findIndex(n => {
|
||||
return !(busy && !(n.forFeedback || n.urgency === Urgency.CRITICAL));
|
||||
});
|
||||
const nextNotification = this._notificationQueue[nextIndex];
|
||||
|
||||
if (nextIndex > -1)
|
||||
this._notificationQueue.splice(nextIndex, 1);
|
||||
|
||||
this._showNotification(nextNotification);
|
||||
|
||||
if (prevQueueLength !== this._notificationQueue)
|
||||
this.emit('queue-changed');
|
||||
}
|
||||
|
||||
let hasNotifications = Main.sessionMode.hasNotifications;
|
||||
_showNotification(notification) {
|
||||
if (this._notification && this._notification !== notification)
|
||||
this._hideNotification(this._notification);
|
||||
|
||||
if (this._notificationState === State.HIDDEN) {
|
||||
let nextNotification = this._notificationQueue[0] || null;
|
||||
if (hasNotifications && nextNotification) {
|
||||
let limited = this._busy || Main.layoutManager.primaryMonitor.inFullscreen;
|
||||
let showNextNotification = !limited || nextNotification.forFeedback || nextNotification.urgency === Urgency.CRITICAL;
|
||||
if (showNextNotification)
|
||||
this._showNotification();
|
||||
}
|
||||
} else if (this._notificationState === State.SHOWING ||
|
||||
this._notificationState === State.SHOWN) {
|
||||
let expired = (this._userActiveWhileNotificationShown &&
|
||||
this._notificationTimeoutId === 0 &&
|
||||
this._notification.urgency !== Urgency.CRITICAL &&
|
||||
!this._pointerInNotification) || this._notificationExpired;
|
||||
let mustClose = this._notificationRemoved || !hasNotifications || expired;
|
||||
if (!notification)
|
||||
return;
|
||||
|
||||
if (mustClose) {
|
||||
let animate = hasNotifications && !this._notificationRemoved;
|
||||
this._hideNotification(animate);
|
||||
} else if (this._notificationState === State.SHOWN &&
|
||||
this._pointerInNotification) {
|
||||
if (!this._banner.expanded)
|
||||
this._expandBanner(false);
|
||||
const updatingNotification = !!this._notification;
|
||||
if (!updatingNotification) {
|
||||
this._banner = new Calendar.NotificationMessage(notification);
|
||||
this._banner.can_focus = false;
|
||||
this._banner.add_style_class_name('notification-banner');
|
||||
this._banner.alwaysShowButtons = true;
|
||||
this._bannerBin.add_child(this._banner);
|
||||
this._bannerBin.opacity = 0;
|
||||
this._bannerBin.y = -this._banner.height;
|
||||
this._notification = notification;
|
||||
|
||||
notification.source.policy.connectObject('notify::show-banners', () => {
|
||||
if (notification.source.policy.showBanners)
|
||||
this.showNotification(notification);
|
||||
else
|
||||
this._ensureBannerFocused();
|
||||
}
|
||||
this._hideNotfication(notification);
|
||||
}, this);
|
||||
} else {
|
||||
// If the notification was shown already reset the timeout
|
||||
this._clearNotificationTimeout();
|
||||
}
|
||||
|
||||
this._updatingState = false;
|
||||
|
||||
// Clean transient variables that are used to communicate actions
|
||||
// to updateState()
|
||||
this._notificationExpired = false;
|
||||
}
|
||||
|
||||
_onIdleMonitorBecameActive() {
|
||||
this._userActiveWhileNotificationShown = true;
|
||||
this._updateNotificationTimeout(2000);
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
_showNotification() {
|
||||
this._notification = this._notificationQueue.shift();
|
||||
this.emit('queue-changed');
|
||||
|
||||
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(this._onIdleMonitorBecameActive.bind(this));
|
||||
}
|
||||
|
||||
this._banner = new Calendar.NotificationMessage(this._notification);
|
||||
this._banner.can_focus = false;
|
||||
this._banner._header.expandButton.visible = false;
|
||||
this._banner.add_style_class_name('notification-banner');
|
||||
|
||||
this._bannerBin.add_child(this._banner);
|
||||
|
||||
this._bannerBin.opacity = 0;
|
||||
this._bannerBin.y = -this._banner.height;
|
||||
this.show();
|
||||
|
||||
Meta.disable_unredirect_for_display(global.display);
|
||||
this._updateShowingNotification();
|
||||
|
||||
let [x, y] = 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() {
|
||||
this._notification.acknowledged = true;
|
||||
this._notification.playSound();
|
||||
notification.acknowledged = true;
|
||||
notification.playSound();
|
||||
|
||||
// We auto-expand notifications with CRITICAL urgency, or for which the relevant setting
|
||||
// is on in the control center.
|
||||
if (this._notification.urgency === Urgency.CRITICAL ||
|
||||
this._notification.source.policy.forceExpanded)
|
||||
this._expandBanner(true);
|
||||
if (notification.urgency === Urgency.CRITICAL ||
|
||||
notification.source.policy.forceExpanded)
|
||||
this._banner.expand(false);
|
||||
else
|
||||
this._banner.unexpand(false);
|
||||
|
||||
// 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.
|
||||
|
|
@ -1159,11 +1004,7 @@ export const MessageTray = GObject.registerClass({
|
|||
// (which is banner height or expanded height, depending on the notification state)
|
||||
// This ensures that both new notifications and notifications in the banner mode that might
|
||||
// have been in the process of hiding are shown with the correct height.
|
||||
//
|
||||
// We use this._showNotificationCompleted() onComplete callback to extend the time the updated
|
||||
// notification is being shown.
|
||||
|
||||
this._notificationState = State.SHOWING;
|
||||
this._bannerBin.remove_all_transitions();
|
||||
this._bannerBin.ease({
|
||||
opacity: 255,
|
||||
|
|
@ -1175,65 +1016,52 @@ export const MessageTray = GObject.registerClass({
|
|||
duration: ANIMATION_TIME,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_BACK,
|
||||
onComplete: () => {
|
||||
this._notificationState = State.SHOWN;
|
||||
this._showNotificationCompleted();
|
||||
this._updateState();
|
||||
this._showNotificationCompleted(notification);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_showNotificationCompleted() {
|
||||
if (this._notification.urgency !== Urgency.CRITICAL)
|
||||
this._updateNotificationTimeout(NOTIFICATION_TIMEOUT);
|
||||
_showNotificationCompleted(notification) {
|
||||
if (notification.urgency === Urgency.CRITICAL)
|
||||
return;
|
||||
|
||||
const userIdle = this.idleMonitor.get_idletime() > IDLE_TIME;
|
||||
if (userIdle) {
|
||||
this.idleMonitor.add_user_active_watch(() => {
|
||||
this._showNotificationCompleted(notification);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._startNotificationTimeout();
|
||||
}
|
||||
|
||||
_updateNotificationTimeout(timeout) {
|
||||
_clearNotificationTimeout() {
|
||||
if (this._notificationTimeoutId) {
|
||||
GLib.source_remove(this._notificationTimeoutId);
|
||||
this._notificationTimeoutId = 0;
|
||||
}
|
||||
if (timeout > 0) {
|
||||
this._notificationTimeoutId =
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, timeout,
|
||||
this._notificationTimeout.bind(this));
|
||||
GLib.Source.set_name_by_id(this._notificationTimeoutId, '[gnome-shell] this._notificationTimeout');
|
||||
}
|
||||
}
|
||||
|
||||
_notificationTimeout() {
|
||||
let [x, y] = 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;
|
||||
_startNotificationTimeout() {
|
||||
this._notificationTimeoutId =
|
||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, NOTIFICATION_TIMEOUT, () => {
|
||||
this._showNextNotification();
|
||||
return GLib.SOURCE_REMOVE;
|
||||
});
|
||||
GLib.Source.set_name_by_id(this._notificationTimeoutId, '[gnome-shell] this._notificationTimeout');
|
||||
}
|
||||
|
||||
_hideNotification(animate) {
|
||||
_hideNotification(notification) {
|
||||
// Don't do anything if the notification that should be hidden isn't shown
|
||||
if (!this._notification || this._notification !== notification)
|
||||
return;
|
||||
|
||||
this._notificationFocusGrabber.ungrabFocus();
|
||||
|
||||
this._banner.disconnectObject(this);
|
||||
|
||||
this._resetNotificationLeftTimeout();
|
||||
this._bannerBin.remove_all_transitions();
|
||||
this._clearNotificationTimeout();
|
||||
|
||||
const duration = animate ? ANIMATION_TIME : 0;
|
||||
this._notificationState = State.HIDING;
|
||||
const duration = ANIMATION_TIME;
|
||||
this._bannerBin.ease({
|
||||
opacity: 0,
|
||||
duration,
|
||||
|
|
@ -1244,46 +1072,25 @@ export const MessageTray = GObject.registerClass({
|
|||
duration,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_BACK,
|
||||
onComplete: () => {
|
||||
this._notificationState = State.HIDDEN;
|
||||
this._hideNotificationCompleted();
|
||||
this._updateState();
|
||||
this._hideNotificationCompleted(notification);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
_hideNotificationCompleted() {
|
||||
let notification = this._notification;
|
||||
this._notification = null;
|
||||
if (!this._notificationRemoved && notification.isTransient)
|
||||
notification.destroy(NotificationDestroyedReason.EXPIRED);
|
||||
|
||||
this._pointerInNotification = false;
|
||||
this._notificationRemoved = false;
|
||||
_hideNotificationCompleted(notification) {
|
||||
Meta.enable_unredirect_for_display(global.display);
|
||||
|
||||
this._banner.destroy();
|
||||
this._banner = null;
|
||||
this.hide();
|
||||
}
|
||||
if (this._notification === notification) {
|
||||
notification.disconnectObject(this);
|
||||
notification.source.policy.disconnectObject(this);
|
||||
this.hide();
|
||||
this._banner?.destroy();
|
||||
this._banner = null;
|
||||
this._notification = null;
|
||||
}
|
||||
|
||||
_expandActiveNotification() {
|
||||
if (!this._banner)
|
||||
return;
|
||||
|
||||
this._expandBanner(false);
|
||||
}
|
||||
|
||||
_expandBanner(autoExpanding) {
|
||||
// Don't animate changes in notifications that are auto-expanding.
|
||||
this._banner.expand(!autoExpanding);
|
||||
|
||||
// Don't focus notifications that are auto-expanding.
|
||||
if (!autoExpanding)
|
||||
this._ensureBannerFocused();
|
||||
}
|
||||
|
||||
_ensureBannerFocused() {
|
||||
this._notificationFocusGrabber.grabFocus();
|
||||
if (notification.isTransient)
|
||||
notification.destroy(NotificationDestroyedReason.EXPIRED);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue