[gnome-shell/wip/carlosg/grabs-pt2: 11/11] popupMenu: Refactor focus and key management
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/carlosg/grabs-pt2: 11/11] popupMenu: Refactor focus and key management
- Date: Fri, 19 Nov 2021 13:23:30 +0000 (UTC)
commit e768932280361d0df475611f781b84753ee91300
Author: Carlos Garnacho <carlosg gnome org>
Date: Thu Nov 18 01:22:33 2021 +0100
popupMenu: Refactor focus and key management
With the presence of Clutter.grab(), this behaves differently enough
that needs some redoing. The larger difference is what actors are
eligible for handling events.
In the older code, a PopupMenuManager would ask the grabHelper to
capture events from all the stage, and selectively silence events
on any actor that is not the currently shown popup menu or the
"source" actor for any other popup in the group (i.e. those that
would pop up another menu).
But we don't want to just silence events, we want to emit the
correct set of crossing events when a popup menu is shown or closed,
this requires a ClutterGrab() on the currently shown menu. Since
the presence of a grab also affects the hability to have actors
outside the grab area to handle events, the PopupMenuManager now
must detect hovering and focus changes to other menu sources by
handling events on the grabbed popup itself.
Otherwise, there is still a backing GrabHelper and pushModal(),
to also leverage proper keyboard focus restoration after the
(last) popup is closed.
js/ui/popupMenu.js | 103 +++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 80 insertions(+), 23 deletions(-)
---
diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js
index 9eae413070..453f2e629e 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -886,12 +886,10 @@ var PopupMenu = class extends PopupMenuBase {
return Clutter.EVENT_PROPAGATE;
let symbol = event.get_key_symbol();
+
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.toggle();
return Clutter.EVENT_STOP;
- } else if (symbol == Clutter.KEY_Escape && this.isOpen) {
- this.close();
- return Clutter.EVENT_STOP;
} else if (symbol == navKey) {
if (!this.isOpen)
this.toggle();
@@ -930,6 +928,7 @@ var PopupMenu = class extends PopupMenuBase {
this.actor.get_parent().set_child_above_sibling(this.actor, null);
this.emit('open-state-changed', true);
+ this._grab = Clutter.grab(global.stage, this.actor);
}
close(animate) {
@@ -945,6 +944,8 @@ var PopupMenu = class extends PopupMenuBase {
if (!this.isOpen)
return;
+ Clutter.ungrab(this._grab);
+ this._grab = null;
this.isOpen = false;
this.emit('open-state-changed', false);
}
@@ -1286,6 +1287,16 @@ var PopupMenuManager = class {
grabParams = Params.parse(grabParams,
{ actionMode: Shell.ActionMode.POPUP });
this._grabHelper = new GrabHelper.GrabHelper(owner, grabParams);
+ global.stage.connect('notify::key-focus', () => {
+ if (!this.activeMenu)
+ return;
+
+ let actor = global.stage.get_key_focus();
+ let newMenu = this._findMenuForSource(actor);
+
+ if (newMenu)
+ this._changeMenu(newMenu);
+ });
this._menus = [];
}
@@ -1297,19 +1308,12 @@ var PopupMenuManager = class {
menu,
openStateChangeId: menu.connect('open-state-changed', this._onMenuOpenState.bind(this)),
destroyId: menu.connect('destroy', this._onMenuDestroy.bind(this)),
- enterId: 0,
- focusInId: 0,
+ capturedEventId: menu.actor.connect('captured-event', this._onCapturedEvent.bind(this)),
};
let source = menu.sourceActor;
- if (source) {
+ if (source)
this._grabHelper.addActor(source);
- menudata.enterId = source.connect('enter-event',
- () => this._onMenuSourceEnter(menu));
- menudata.focusInId = source.connect('key-focus-in', () => {
- this._onMenuSourceEnter(menu);
- });
- }
if (position == undefined)
this._menus.push(menudata);
@@ -1329,11 +1333,6 @@ var PopupMenuManager = class {
menu.disconnect(menudata.openStateChangeId);
menu.disconnect(menudata.destroyId);
- if (menudata.enterId)
- menu.sourceActor.disconnect(menudata.enterId);
- if (menudata.focusInId)
- menu.sourceActor.disconnect(menudata.focusInId);
-
if (menu.sourceActor)
this._grabHelper.removeActor(menu.sourceActor);
this._menus.splice(position, 1);
@@ -1371,17 +1370,75 @@ var PopupMenuManager = class {
: BoxPointer.PopupAnimation.FULL);
}
- _onMenuSourceEnter(menu) {
- if (!this._grabHelper.grabbed)
- return Clutter.EVENT_PROPAGATE;
+ _onCapturedEvent(actor, event) {
+ let menu = actor._delegate;
+ if (event.type() == Clutter.EventType.KEY_PRESS) {
+ let state = event.get_state();
+
+ let navKey;
+ switch (menu._boxPointer.arrowSide) {
+ case St.Side.TOP:
+ navKey = Clutter.KEY_Down;
+ break;
+ case St.Side.BOTTOM:
+ navKey = Clutter.KEY_Up;
+ break;
+ case St.Side.LEFT:
+ navKey = Clutter.KEY_Right;
+ break;
+ case St.Side.RIGHT:
+ navKey = Clutter.KEY_Left;
+ break;
+ }
- if (this._grabHelper.isActorGrabbed(menu.actor))
- return Clutter.EVENT_PROPAGATE;
+ // if user has a modifier down (except capslock and numlock)
+ // then don't handle the key press here
+ state &= ~Clutter.ModifierType.LOCK_MASK;
+ state &= ~Clutter.ModifierType.MOD2_MASK;
+ state &= Clutter.ModifierType.MODIFIER_MASK;
+
+ if (state)
+ return Clutter.EVENT_PROPAGATE;
+
+ let symbol = event.get_key_symbol();
+ if (symbol === navKey &&
+ !actor.contains(global.stage.get_key_focus())) {
+ actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
+ return Clutter.EVENT_STOP;
+ } else if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
+ menu.toggle();
+ return Clutter.EVENT_STOP;
+ } else if (symbol == Clutter.KEY_Escape && menu.isOpen) {
+ menu.close(BoxPointer.PopupAnimation.FULL);
+ return Clutter.EVENT_STOP;
+ }
+ } else if (event.type() == Clutter.EventType.ENTER) {
+ let hoveredMenu = this._findMenuForSource(event.get_source());
+
+ if (hoveredMenu && hoveredMenu !== menu)
+ this._changeMenu(hoveredMenu);
+ } else if ((event.type() === Clutter.EventType.BUTTON_PRESS ||
+ event.type() === Clutter.EventType.TOUCH_BEGIN) &&
+ !actor.contains(event.get_source())) {
+ menu.close(BoxPointer.PopupAnimation.FULL);
+ }
- this._changeMenu(menu);
return Clutter.EVENT_PROPAGATE;
}
+ _findMenuForSource(actor) {
+ while (actor) {
+ for (let i = 0; i < this._menus.length; i++) {
+ let menudata = this._menus[i];
+ if (menudata.menu.sourceActor == actor)
+ return menudata.menu;
+ }
+ actor = actor.get_parent();
+ }
+
+ return null;
+ }
+
_onMenuDestroy(menu) {
this.removeMenu(menu);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]