messageTray: forward clicks on trayicon SummaryItems to the icon
If the user clicks on the title of a trayicon's SummaryItem, forward that click to the trayicon. Also adjust gnome_shell_plugin_xevent_filter() so that if the trayicon takes a grab as a result of this, we don't hide the message tray. https://bugzilla.gnome.org/show_bug.cgi?id=630842
This commit is contained in:
parent
a40daa3c22
commit
7f17fcfafc
@ -874,6 +874,14 @@ Source.prototype = {
|
|||||||
this.emit('destroy');
|
this.emit('destroy');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// A subclass can redefine this to "steal" clicks from the
|
||||||
|
// summaryitem; Use Clutter.get_current_event() to get the
|
||||||
|
// details, return true to prevent the default handling from
|
||||||
|
// ocurring.
|
||||||
|
handleSummaryClick: function() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
//// Protected methods ////
|
//// Protected methods ////
|
||||||
|
|
||||||
// The subclass must call this at least once to set the summary icon.
|
// The subclass must call this at least once to set the summary icon.
|
||||||
@ -903,6 +911,7 @@ SummaryItem.prototype = {
|
|||||||
this.source = source;
|
this.source = source;
|
||||||
this.actor = new St.Button({ style_class: 'summary-source-button',
|
this.actor = new St.Button({ style_class: 'summary-source-button',
|
||||||
reactive: true,
|
reactive: true,
|
||||||
|
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO | St.ButtonMask.THREE,
|
||||||
track_hover: true });
|
track_hover: true });
|
||||||
|
|
||||||
this._sourceBox = new St.BoxLayout({ style_class: 'summary-source' });
|
this._sourceBox = new St.BoxLayout({ style_class: 'summary-source' });
|
||||||
@ -1165,9 +1174,9 @@ MessageTray.prototype = {
|
|||||||
this._onSummaryItemHoverChanged(summaryItem);
|
this._onSummaryItemHoverChanged(summaryItem);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
summaryItem.actor.connect('button-press-event', Lang.bind(this,
|
summaryItem.actor.connect('clicked', Lang.bind(this,
|
||||||
function (actor, event) {
|
function (actor, button) {
|
||||||
this._onSummaryItemClicked(summaryItem, event);
|
this._onSummaryItemClicked(summaryItem, button);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
|
source.connect('destroy', Lang.bind(this, this._onSourceDestroy));
|
||||||
@ -1404,13 +1413,14 @@ MessageTray.prototype = {
|
|||||||
this._expandedSummaryItem.setEllipsization(Pango.EllipsizeMode.END);
|
this._expandedSummaryItem.setEllipsization(Pango.EllipsizeMode.END);
|
||||||
},
|
},
|
||||||
|
|
||||||
_onSummaryItemClicked: function(summaryItem, event) {
|
_onSummaryItemClicked: function(summaryItem, button) {
|
||||||
let clickedButton = event.get_button();
|
if (summaryItem.source.handleSummaryClick())
|
||||||
if (!this._clickedSummaryItem ||
|
this._unsetClickedSummaryItem();
|
||||||
this._clickedSummaryItem != summaryItem ||
|
else if (!this._clickedSummaryItem ||
|
||||||
this._clickedSummaryItemMouseButton != clickedButton) {
|
this._clickedSummaryItem != summaryItem ||
|
||||||
|
this._clickedSummaryItemMouseButton != button) {
|
||||||
this._clickedSummaryItem = summaryItem;
|
this._clickedSummaryItem = summaryItem;
|
||||||
this._clickedSummaryItemMouseButton = clickedButton;
|
this._clickedSummaryItemMouseButton = button;
|
||||||
} else {
|
} else {
|
||||||
this._unsetClickedSummaryItem();
|
this._unsetClickedSummaryItem();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||||
|
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
const DBus = imports.dbus;
|
const DBus = imports.dbus;
|
||||||
const GLib = imports.gi.GLib;
|
const GLib = imports.gi.GLib;
|
||||||
const Lang = imports.lang;
|
const Lang = imports.lang;
|
||||||
@ -441,7 +442,7 @@ Source.prototype = {
|
|||||||
this.title = this.app.get_name();
|
this.title = this.app.get_name();
|
||||||
else
|
else
|
||||||
this.useNotificationIcon = true;
|
this.useNotificationIcon = true;
|
||||||
this._isTrayIcon = false;
|
this._trayIcon = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
notify: function(notification, icon) {
|
notify: function(notification, icon) {
|
||||||
@ -452,6 +453,18 @@ Source.prototype = {
|
|||||||
MessageTray.Source.prototype.notify.call(this, notification);
|
MessageTray.Source.prototype.notify.call(this, notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleSummaryClick: function() {
|
||||||
|
if (!this._trayIcon)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let event = Clutter.get_current_event();
|
||||||
|
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this._trayIcon.click(event);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
_setApp: function() {
|
_setApp: function() {
|
||||||
if (this.app)
|
if (this.app)
|
||||||
return;
|
return;
|
||||||
@ -466,7 +479,7 @@ Source.prototype = {
|
|||||||
|
|
||||||
// Only override the icon if we were previously using
|
// Only override the icon if we were previously using
|
||||||
// notification-based icons (ie, not a trayicon) or if it was unset before
|
// notification-based icons (ie, not a trayicon) or if it was unset before
|
||||||
if (!this._isTrayIcon) {
|
if (!this._trayIcon) {
|
||||||
this.useNotificationIcon = false;
|
this.useNotificationIcon = false;
|
||||||
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
|
||||||
}
|
}
|
||||||
@ -475,7 +488,7 @@ Source.prototype = {
|
|||||||
setTrayIcon: function(icon) {
|
setTrayIcon: function(icon) {
|
||||||
this._setSummaryIcon(icon);
|
this._setSummaryIcon(icon);
|
||||||
this.useNotificationIcon = false;
|
this.useNotificationIcon = false;
|
||||||
this._isTrayIcon = true;
|
this._trayIcon = icon;
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function(notification) {
|
open: function(notification) {
|
||||||
@ -483,7 +496,7 @@ Source.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_notificationRemoved: function() {
|
_notificationRemoved: function() {
|
||||||
if (!this._isTrayIcon)
|
if (!this._trayIcon)
|
||||||
this.destroy();
|
this.destroy();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -321,20 +321,28 @@ gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* When the pointer leaves the stage to enter a child of the stage
|
|
||||||
* (like a notification icon), we don't want to produce Clutter leave
|
|
||||||
* events. But Clutter treats all leave events identically, so we
|
|
||||||
* need hide the detail = NotifyInferior events from it.
|
|
||||||
*
|
|
||||||
* Since Clutter doesn't see any event at all, this does mean that
|
|
||||||
* it won't produce an enter event on a Clutter actor that surrounds
|
|
||||||
* the child (unless it gets a MotionNotify before the Enter event).
|
|
||||||
* Other weirdness is likely also possible.
|
|
||||||
*/
|
|
||||||
if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify)
|
if ((xev->xany.type == EnterNotify || xev->xany.type == LeaveNotify)
|
||||||
&& xev->xcrossing.detail == NotifyInferior
|
|
||||||
&& xev->xcrossing.window == clutter_x11_get_stage_window (CLUTTER_STAGE (clutter_stage_get_default ())))
|
&& xev->xcrossing.window == clutter_x11_get_stage_window (CLUTTER_STAGE (clutter_stage_get_default ())))
|
||||||
return TRUE;
|
{
|
||||||
|
/* If the pointer enters a child of the stage window (eg, a
|
||||||
|
* trayicon), we want to consider it to still be in the stage,
|
||||||
|
* so don't let Clutter see the event.
|
||||||
|
*/
|
||||||
|
if (xev->xcrossing.detail == NotifyInferior)
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* If the pointer is grabbed by a window it is not currently in,
|
||||||
|
* filter that out as well. In particular, if a trayicon grabs
|
||||||
|
* the pointer after a click on its label, we don't want to hide
|
||||||
|
* the message tray. Filtering out this event will leave Clutter
|
||||||
|
* out of sync, but that happens fairly often with grabs, and we
|
||||||
|
* can work around it. (Eg, shell_global_sync_pointer().)
|
||||||
|
*/
|
||||||
|
if (xev->xcrossing.mode == NotifyGrab &&
|
||||||
|
(xev->xcrossing.detail == NotifyNonlinear ||
|
||||||
|
xev->xcrossing.detail == NotifyNonlinearVirtual))
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pass the event to shell-global
|
* Pass the event to shell-global
|
||||||
|
@ -165,3 +165,76 @@ shell_tray_icon_new (ShellEmbeddedWindow *window)
|
|||||||
"window", window,
|
"window", window,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* shell_tray_icon_click:
|
||||||
|
* @icon: a #ShellTrayIcon
|
||||||
|
* @event: the #ClutterEvent triggering the fake click
|
||||||
|
*
|
||||||
|
* Fakes a press and release on @icon. @event must be a
|
||||||
|
* %CLUTTER_BUTTON_RELEASE event. Its relevant details will be passed
|
||||||
|
* on to the icon, but its coordinates will be ignored; the click is
|
||||||
|
* always made on the center of @icon.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
shell_tray_icon_click (ShellTrayIcon *icon,
|
||||||
|
ClutterEvent *event)
|
||||||
|
{
|
||||||
|
XButtonEvent xbevent;
|
||||||
|
XCrossingEvent xcevent;
|
||||||
|
GdkWindow *remote_window;
|
||||||
|
GdkScreen *screen;
|
||||||
|
int x_root, y_root;
|
||||||
|
Display *xdisplay;
|
||||||
|
Window xwindow, xrootwindow;
|
||||||
|
|
||||||
|
g_return_if_fail (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE);
|
||||||
|
|
||||||
|
gdk_error_trap_push ();
|
||||||
|
|
||||||
|
remote_window = gtk_socket_get_plug_window (GTK_SOCKET (icon->priv->socket));
|
||||||
|
xwindow = GDK_WINDOW_XID (remote_window);
|
||||||
|
xdisplay = GDK_WINDOW_XDISPLAY (remote_window);
|
||||||
|
screen = gdk_window_get_screen (remote_window);
|
||||||
|
xrootwindow = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
|
||||||
|
gdk_window_get_origin (remote_window, &x_root, &y_root);
|
||||||
|
|
||||||
|
/* First make the icon believe the pointer is inside it */
|
||||||
|
xcevent.type = EnterNotify;
|
||||||
|
xcevent.window = xwindow;
|
||||||
|
xcevent.root = xrootwindow;
|
||||||
|
xcevent.subwindow = None;
|
||||||
|
xcevent.time = clutter_event_get_time (event);
|
||||||
|
xcevent.x = gdk_window_get_width (remote_window) / 2;
|
||||||
|
xcevent.y = gdk_window_get_height (remote_window) / 2;
|
||||||
|
xcevent.x_root = x_root + xcevent.x;
|
||||||
|
xcevent.y_root = y_root + xcevent.y;
|
||||||
|
xcevent.mode = NotifyNormal;
|
||||||
|
xcevent.detail = NotifyNonlinear;
|
||||||
|
xcevent.same_screen = True;
|
||||||
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
|
||||||
|
|
||||||
|
/* Now do the click */
|
||||||
|
xbevent.type = ButtonPress;
|
||||||
|
xbevent.window = xwindow;
|
||||||
|
xbevent.root = xrootwindow;
|
||||||
|
xbevent.subwindow = None;
|
||||||
|
xbevent.time = xcevent.time;
|
||||||
|
xbevent.x = xcevent.x;
|
||||||
|
xbevent.y = xcevent.y;
|
||||||
|
xbevent.x_root = xcevent.x_root;
|
||||||
|
xbevent.y_root = xcevent.y_root;
|
||||||
|
xbevent.state = clutter_event_get_state (event);
|
||||||
|
xbevent.button = clutter_event_get_button (event);
|
||||||
|
xbevent.same_screen = True;
|
||||||
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
|
||||||
|
|
||||||
|
xbevent.type = ButtonRelease;
|
||||||
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xbevent);
|
||||||
|
|
||||||
|
/* And move the pointer back out */
|
||||||
|
xcevent.type = LeaveNotify;
|
||||||
|
XSendEvent (xdisplay, xwindow, False, 0, (XEvent *)&xcevent);
|
||||||
|
|
||||||
|
gdk_error_trap_pop_ignored ();
|
||||||
|
}
|
||||||
|
@ -31,4 +31,7 @@ struct _ShellTrayIconClass
|
|||||||
GType shell_tray_icon_get_type (void) G_GNUC_CONST;
|
GType shell_tray_icon_get_type (void) G_GNUC_CONST;
|
||||||
ClutterActor *shell_tray_icon_new (ShellEmbeddedWindow *window);
|
ClutterActor *shell_tray_icon_new (ShellEmbeddedWindow *window);
|
||||||
|
|
||||||
|
void shell_tray_icon_click (ShellTrayIcon *icon,
|
||||||
|
ClutterEvent *event);
|
||||||
|
|
||||||
#endif /* __SHELL_TRAY_ICON_H__ */
|
#endif /* __SHELL_TRAY_ICON_H__ */
|
||||||
|
Loading…
Reference in New Issue
Block a user