Skip to content

Commit 58e1c47

Browse files
committed
keybindings: Improve consistency during modal states, fix layout
switching. - Move ime pre-edit workaround from the menu applet to the StEntry class so it can benefit all users of the class. - Remove another old keybinding workaround from the menu applet. (8a0d2d9) - Make non-XKB-option layout-switching usable in all Action modes - Handle the RunDialog's super-to-cancel on key-release instead of key-press. - Have Main._stageEventHandler trigger modifier-only keybindings on key-release instead of -press. The last item ends up recreating modifier-only keybinding handling that currently exists in muffin's keybindings.c. The most important part is that it identifies a modifier as the keyval on key-press but only stores the keybinding action if there's a match. If there's a subsequent key-press of a normal key, that keybinding is triggered if one exists. If the next event is a release, the stored action is triggered instead.
1 parent af735cb commit 58e1c47

5 files changed

Lines changed: 123 additions & 26 deletions

File tree

files/usr/share/cinnamon/applets/menu@cinnamon.org/applet.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,19 +1833,6 @@ class CinnamonMenuApplet extends Applet.TextIconApplet {
18331833
}
18341834
}
18351835

1836-
/* check for a keybinding and quit early, otherwise we get a double hit
1837-
of the keybinding callback */
1838-
let action = global.display.get_keybinding_action(keyCode, modifierState);
1839-
1840-
if (action == Meta.KeyBindingAction.CUSTOM) {
1841-
return Clutter.EVENT_STOP;
1842-
}
1843-
1844-
if (this.searchEntryText.has_preedit()) {
1845-
// There is an uncommitted text in the search box, let the input method to handle this.
1846-
return Clutter.EVENT_PROPAGATE;
1847-
}
1848-
18491836
let ctrlKey = modifierState & Clutter.ModifierType.CONTROL_MASK;
18501837

18511838
// If a context menu is open, hijack keyboard navigation and concentrate on the context menu.

js/ui/keyboardManager.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -530,14 +530,30 @@ var InputSourceManager = class {
530530

531531
_setupKeybindings() {
532532
let kb = this._kb_settings.get_strv("switch-input-source");
533-
Main.keybindingManager.addHotKeyArray("switch-input-source", kb, () => this._modifiersSwitcher(false));
533+
Main.keybindingManager.addHotKeyArray(
534+
"switch-input-source", kb,
535+
() => this._modifiersSwitcher(false),
536+
undefined,
537+
Cinnamon.ActionMode.ALL
538+
);
539+
534540
kb = this._kb_settings.get_strv("switch-input-source-backward");
535-
Main.keybindingManager.addHotKeyArray("switch-input-source-backward", kb, () => this._modifiersSwitcher(true));
541+
Main.keybindingManager.addHotKeyArray(
542+
"switch-input-source-backward", kb,
543+
() => this._modifiersSwitcher(true),
544+
undefined,
545+
Cinnamon.ActionMode.ALL
546+
);
536547

537548
for (let i = 0; i <= 3; i++) {
538549
const name = `switch-input-source-${i}`;
539550
kb = this._kb_settings.get_strv(name);
540-
Main.keybindingManager.addHotKeyArray(name, kb, () => this.activateInputSourceIndex(i));
551+
Main.keybindingManager.addHotKeyArray(
552+
name, kb,
553+
() => this.activateInputSourceIndex(i),
554+
undefined,
555+
Cinnamon.ActionMode.ALL
556+
);
541557
}
542558
}
543559

js/ui/main.js

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,33 +1278,91 @@ function _shouldFilterKeybinding(entry) {
12781278
return !allowed;
12791279
}
12801280

1281-
// This function encapsulates hacks to make certain global keybindings
1282-
// work even when we are in one of our modes where global keybindings
1283-
// are disabled with a global grab. (When there is a global grab, then
1284-
// all key events will be delivered to the stage, so ::captured-event
1285-
// on the stage can be used for global keybindings.)
1281+
/* This mimics some of the behavior in muffin's keybindings.c, to allow multi-key keybindings
1282+
* to work properly while pushModal is active and our input events are bypassing muffin's normal
1283+
* event handling.
1284+
*
1285+
* In normal input modes, single key (modifier) keybindings are activated on key-release, and multi-
1286+
* key bindings are activated on key-press.
1287+
*
1288+
* This needs to work in our pushModal state also, for things like layout-switching. We check the
1289+
* keyval of the event - if it's a modifier key it can potentially be a single-key binding or part
1290+
* of a multi-key binding, so we defer activating any single-key modifier bindings until key-release,
1291+
* to give the multi-key binding a chance to activate on key-press.
1292+
*/
1293+
let _modifierOnlyAction = 0;
1294+
1295+
function _isModifierKeyval(symbol) {
1296+
return symbol === Clutter.KEY_Super_L || symbol === Clutter.KEY_Super_R ||
1297+
symbol === Clutter.KEY_Control_L || symbol === Clutter.KEY_Control_R ||
1298+
symbol === Clutter.KEY_Alt_L || symbol === Clutter.KEY_Alt_R ||
1299+
symbol === Clutter.KEY_Shift_L || symbol === Clutter.KEY_Shift_R;
1300+
}
1301+
12861302
function _stageEventHandler(actor, event) {
12871303
if (modalCount == 0)
12881304
return false;
12891305

1290-
if (event.type() != Clutter.EventType.KEY_PRESS) {
1291-
if(!popup_rendering_actor || event.type() != Clutter.EventType.BUTTON_RELEASE)
1306+
let eventType = event.type();
1307+
1308+
if (eventType !== Clutter.EventType.KEY_PRESS &&
1309+
eventType !== Clutter.EventType.KEY_RELEASE) {
1310+
if (!popup_rendering_actor || eventType !== Clutter.EventType.BUTTON_RELEASE)
12921311
return false;
12931312
return (event.get_source() && popup_rendering_actor.contains(event.get_source()));
12941313
}
12951314

1315+
if (event.get_source() instanceof Clutter.Text &&
1316+
(event.get_flags() & Clutter.EventFlags.INPUT_METHOD)) {
1317+
return false;
1318+
}
1319+
12961320
let keyCode = event.get_key_code();
12971321
let modifierState = Cinnamon.get_event_state(event);
12981322

1299-
let action = global.display.get_keybinding_action(keyCode, modifierState);
1300-
if (!(event.get_source() instanceof Clutter.Text && (event.get_flags() & Clutter.EventFlags.INPUT_METHOD))) {
1323+
if (eventType === Clutter.EventType.KEY_PRESS) {
1324+
if (_isModifierKeyval(event.get_key_symbol())) {
1325+
let action = global.display.get_keybinding_action(keyCode, modifierState);
1326+
if (action > 0) {
1327+
let entry = keybindingManager.getBindingById(action);
1328+
if (!_shouldFilterKeybinding(entry)) {
1329+
_modifierOnlyAction = action;
1330+
return true;
1331+
}
1332+
}
1333+
return false;
1334+
}
1335+
1336+
// A non-modifier key was pressed while a modifier was held.
1337+
// Use -1 to indicate the modifier release should be consumed.
1338+
_modifierOnlyAction = -1;
1339+
1340+
let action = global.display.get_keybinding_action(keyCode, modifierState);
13011341
if (action > 0) {
13021342
let entry = keybindingManager.getBindingById(action);
13031343
if (!_shouldFilterKeybinding(entry)) {
13041344
keybindingManager.invoke_keybinding_action_by_id(action);
13051345
return true;
13061346
}
13071347
}
1348+
1349+
return false;
1350+
}
1351+
1352+
// Release event - activate the single-key keybinding, or eat the release if
1353+
// a multi-key combo was used.
1354+
if (_isModifierKeyval(event.get_key_symbol())) {
1355+
if (_modifierOnlyAction > 0) {
1356+
let action = _modifierOnlyAction;
1357+
_modifierOnlyAction = 0;
1358+
keybindingManager.invoke_keybinding_action_by_id(action);
1359+
return true;
1360+
}
1361+
1362+
if (_modifierOnlyAction === -1) {
1363+
_modifierOnlyAction = 0;
1364+
return true;
1365+
}
13081366
}
13091367

13101368
return false;
@@ -1319,6 +1377,8 @@ function _findModal(actor) {
13191377
}
13201378

13211379
function _completeModalSetup(actor, mode) {
1380+
_modifierOnlyAction = 0;
1381+
13221382
if (modalCount == 0)
13231383
Meta.disable_unredirect_for_display(global.display);
13241384

js/ui/runDialog.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,14 @@ class RunDialog extends ModalDialog.ModalDialog {
187187
this._commandError = false;
188188

189189
this._entryText.connect('key-press-event', this._onKeyPress.bind(this));
190+
this._entryText.connect('key-release-event', (o, e) => {
191+
let symbol = e.get_key_symbol();
192+
if (symbol === Clutter.KEY_Super_L || symbol === Clutter.KEY_Super_R) {
193+
this.close();
194+
return Clutter.EVENT_STOP;
195+
}
196+
return Clutter.EVENT_PROPAGATE;
197+
});
190198

191199
this._history = new History.HistoryManager({
192200
gsettingsKey: HISTORY_KEY,
@@ -230,7 +238,7 @@ class RunDialog extends ModalDialog.ModalDialog {
230238
}
231239
return true;
232240
}
233-
if (symbol === Clutter.KEY_Escape || symbol === Clutter.KEY_Super_L || symbol === Clutter.KEY_Super_R) {
241+
if (symbol === Clutter.KEY_Escape) {
234242
this.close();
235243
return true;
236244
}

src/st/st-entry.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,31 @@ st_entry_key_press_event (ClutterActor *actor,
914914
return CLUTTER_ACTOR_CLASS (st_entry_parent_class)->key_press_event (actor, event);
915915
}
916916

917+
static gboolean
918+
st_entry_captured_event (ClutterActor *actor,
919+
ClutterEvent *event)
920+
{
921+
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
922+
923+
if (clutter_event_type (event) != CLUTTER_KEY_PRESS)
924+
return CLUTTER_EVENT_PROPAGATE;
925+
926+
// Instead of having to check this everywhere an StEntry is used, filter
927+
// the event before it gets to the normal handlers.
928+
//
929+
// Original PR: https://github.com/linuxmint/cinnamon/pull/13317
930+
// ref: https://github.com/linuxmint/mint22.3-beta/issues/60
931+
932+
if (clutter_text_has_preedit (CLUTTER_TEXT (priv->entry)))
933+
{
934+
CLUTTER_ACTOR_GET_CLASS (priv->entry)->key_press_event (priv->entry,
935+
(ClutterKeyEvent *) event);
936+
return CLUTTER_EVENT_STOP;
937+
}
938+
939+
return CLUTTER_EVENT_PROPAGATE;
940+
}
941+
917942
static void
918943
st_entry_key_focus_in (ClutterActor *actor)
919944
{
@@ -997,6 +1022,7 @@ st_entry_class_init (StEntryClass *klass)
9971022
actor_class->allocate = st_entry_allocate;
9981023
actor_class->paint = st_entry_paint;
9991024

1025+
actor_class->captured_event = st_entry_captured_event;
10001026
actor_class->key_press_event = st_entry_key_press_event;
10011027
actor_class->key_focus_in = st_entry_key_focus_in;
10021028

0 commit comments

Comments
 (0)