keyboard: Refactor 'commit' action handling
Move the complex parts of this mechanism from the Keyboard object to the KeyboardController object, replaced by code that is easier to follow. Also, keyval guessing is deferred to a later point in the commit procedure so so it does not happen for a single character only, this way we can send multi-character input through the IM, which is necessary for some OSK layouts. Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7190 Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3162>
This commit is contained in:
parent
f0b18c9ccc
commit
741355e2cd
@ -224,7 +224,7 @@ const Key = GObject.registerClass({
|
|||||||
'pressed': {},
|
'pressed': {},
|
||||||
'released': {},
|
'released': {},
|
||||||
'keyval': {param_types: [GObject.TYPE_UINT]},
|
'keyval': {param_types: [GObject.TYPE_UINT]},
|
||||||
'commit': {param_types: [GObject.TYPE_UINT, GObject.TYPE_STRING]},
|
'commit': {param_types: [GObject.TYPE_STRING]},
|
||||||
},
|
},
|
||||||
}, class Key extends St.BoxLayout {
|
}, class Key extends St.BoxLayout {
|
||||||
_init(params, extendedKeys = []) {
|
_init(params, extendedKeys = []) {
|
||||||
@ -281,11 +281,6 @@ const Key = GObject.registerClass({
|
|||||||
this.keyButton._extendedKeys = this._extendedKeyboard;
|
this.keyButton._extendedKeys = this._extendedKeyboard;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getKeyvalFromString(string) {
|
|
||||||
let unicode = string?.length ? string.charCodeAt(0) : undefined;
|
|
||||||
return Clutter.unicode_to_keysym(unicode);
|
|
||||||
}
|
|
||||||
|
|
||||||
_press(button) {
|
_press(button) {
|
||||||
if (button === this.keyButton) {
|
if (button === this.keyButton) {
|
||||||
this._pressTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
this._pressTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
||||||
@ -320,12 +315,10 @@ const Key = GObject.registerClass({
|
|||||||
if (this._pressed) {
|
if (this._pressed) {
|
||||||
if (this._keyval && button === this.keyButton)
|
if (this._keyval && button === this.keyButton)
|
||||||
this.emit('keyval', this._keyval);
|
this.emit('keyval', this._keyval);
|
||||||
else if (commitString) {
|
else if (commitString)
|
||||||
const keyval = this._getKeyvalFromString(commitString);
|
this.emit('commit', commitString);
|
||||||
this.emit('commit', keyval, commitString);
|
else
|
||||||
} else {
|
|
||||||
console.error('Need keyval or commitString');
|
console.error('Need keyval or commitString');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit('released');
|
this.emit('released');
|
||||||
@ -805,7 +798,7 @@ const EmojiPager = GObject.registerClass({
|
|||||||
key.connect('pressed', () => {
|
key.connect('pressed', () => {
|
||||||
this._currentKey = key;
|
this._currentKey = key;
|
||||||
});
|
});
|
||||||
key.connect('commit', (actor, keyval, str) => {
|
key.connect('commit', (actor, str) => {
|
||||||
if (this._currentKey !== key)
|
if (this._currentKey !== key)
|
||||||
return;
|
return;
|
||||||
this._currentKey = null;
|
this._currentKey = null;
|
||||||
@ -1266,6 +1259,7 @@ export const Keyboard = GObject.registerClass({
|
|||||||
|
|
||||||
this._clearShowIdle();
|
this._clearShowIdle();
|
||||||
|
|
||||||
|
this._keyboardController.oskCompletion = false;
|
||||||
this._keyboardController.destroy();
|
this._keyboardController.destroy();
|
||||||
|
|
||||||
Main.layoutManager.untrackChrome(this);
|
Main.layoutManager.untrackChrome(this);
|
||||||
@ -1276,8 +1270,6 @@ export const Keyboard = GObject.registerClass({
|
|||||||
this._languagePopup.destroy();
|
this._languagePopup.destroy();
|
||||||
this._languagePopup = null;
|
this._languagePopup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
IBusManager.getIBusManager().setCompletionEnabled(false, () => Main.inputMethod.update());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_setupKeyboard() {
|
_setupKeyboard() {
|
||||||
@ -1301,7 +1293,7 @@ export const Keyboard = GObject.registerClass({
|
|||||||
this._emojiSelection.connect('toggle', this._toggleEmoji.bind(this));
|
this._emojiSelection.connect('toggle', this._toggleEmoji.bind(this));
|
||||||
this._emojiSelection.connect('close-request', () => this.close(true));
|
this._emojiSelection.connect('close-request', () => this.close(true));
|
||||||
this._emojiSelection.connect('emoji-selected', (selection, emoji) => {
|
this._emojiSelection.connect('emoji-selected', (selection, emoji) => {
|
||||||
this._keyboardController.commitString(emoji);
|
this._keyboardController.commit(emoji).catch(console.error);
|
||||||
});
|
});
|
||||||
|
|
||||||
this._emojiSelection.hide();
|
this._emojiSelection.hide();
|
||||||
@ -1447,12 +1439,13 @@ export const Keyboard = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key.action !== 'modifier') {
|
if (key.action !== 'modifier') {
|
||||||
button.connect('commit', (_actor, keyval, str) => {
|
button.connect('commit', (_actor, str) => {
|
||||||
this._commitAction(keyval, str).then(() => {
|
this._keyboardController.commit(str, this._modifiers).then(() => {
|
||||||
|
this._disableAllModifiers();
|
||||||
if (layout.mode === 'default' ||
|
if (layout.mode === 'default' ||
|
||||||
(layout.mode === 'latched' && !this._latched))
|
(layout.mode === 'latched' && !this._latched))
|
||||||
this._setActiveLevel('default');
|
this._setActiveLevel('default');
|
||||||
});
|
}).catch(console.error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1510,30 +1503,6 @@ export const Keyboard = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _commitAction(keyval, str) {
|
|
||||||
if (this._modifiers.size === 0 && str !== '' &&
|
|
||||||
keyval && this._oskCompletionEnabled) {
|
|
||||||
if (await Main.inputMethod.handleVirtualKey(keyval))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str === '' || !Main.inputMethod.currentFocus ||
|
|
||||||
(keyval && this._oskCompletionEnabled) ||
|
|
||||||
this._modifiers.size > 0 ||
|
|
||||||
!this._keyboardController.commitString(str, true)) {
|
|
||||||
if (keyval !== 0) {
|
|
||||||
this._forwardModifiers(this._modifiers, Clutter.EventType.KEY_PRESS);
|
|
||||||
this._keyboardController.keyvalPress(keyval);
|
|
||||||
GLib.timeout_add(GLib.PRIORITY_DEFAULT, KEY_RELEASE_TIMEOUT, () => {
|
|
||||||
this._keyboardController.keyvalRelease(keyval);
|
|
||||||
this._forwardModifiers(this._modifiers, Clutter.EventType.KEY_RELEASE);
|
|
||||||
this._disableAllModifiers();
|
|
||||||
return GLib.SOURCE_REMOVE;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_setLatched(latched) {
|
_setLatched(latched) {
|
||||||
this._latched = latched;
|
this._latched = latched;
|
||||||
this._setCurrentLevelLatched(this._currentPage, this._latched);
|
this._setCurrentLevelLatched(this._currentPage, this._latched);
|
||||||
@ -1554,15 +1523,6 @@ export const Keyboard = GObject.registerClass({
|
|||||||
this._setModifierEnabled(keyval, !isActive);
|
this._setModifierEnabled(keyval, !isActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
_forwardModifiers(modifiers, type) {
|
|
||||||
for (const keyval of modifiers) {
|
|
||||||
if (type === Clutter.EventType.KEY_PRESS)
|
|
||||||
this._keyboardController.keyvalPress(keyval);
|
|
||||||
else if (type === Clutter.EventType.KEY_RELEASE)
|
|
||||||
this._keyboardController.keyvalRelease(keyval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_disableAllModifiers() {
|
_disableAllModifiers() {
|
||||||
for (const keyval of this._modifiers)
|
for (const keyval of this._modifiers)
|
||||||
this._setModifierEnabled(keyval, false);
|
this._setModifierEnabled(keyval, false);
|
||||||
@ -1699,8 +1659,7 @@ export const Keyboard = GObject.registerClass({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._oskCompletionEnabled =
|
this._keyboardController.oskCompletion = true;
|
||||||
IBusManager.getIBusManager().setCompletionEnabled(true, () => Main.inputMethod.update());
|
|
||||||
this._clearKeyboardRestTimer();
|
this._clearKeyboardRestTimer();
|
||||||
|
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
@ -1735,8 +1694,7 @@ export const Keyboard = GObject.registerClass({
|
|||||||
if (!this._keyboardVisible)
|
if (!this._keyboardVisible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
IBusManager.getIBusManager().setCompletionEnabled(false, () => Main.inputMethod.update());
|
this._keyboardController.oskCompletion = false;
|
||||||
this._oskCompletionEnabled = false;
|
|
||||||
this._clearKeyboardRestTimer();
|
this._clearKeyboardRestTimer();
|
||||||
|
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
@ -2014,15 +1972,72 @@ class KeyboardController extends Signals.EventEmitter {
|
|||||||
return this._currentSource.xkbId;
|
return this._currentSource.xkbId;
|
||||||
}
|
}
|
||||||
|
|
||||||
commitString(string, fromKey) {
|
_forwardModifiers(modifiers, type) {
|
||||||
if (string == null)
|
for (const keyval of modifiers) {
|
||||||
return false;
|
if (type === Clutter.EventType.KEY_PRESS)
|
||||||
/* Let ibus methods fall through keyval emission */
|
this.keyvalPress(keyval);
|
||||||
if (fromKey && this._currentSource.type === InputSourceManager.INPUT_SOURCE_TYPE_IBUS)
|
else if (type === Clutter.EventType.KEY_RELEASE)
|
||||||
return false;
|
this.keyvalRelease(keyval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Main.inputMethod.commit(string);
|
_getKeyvalsFromString(string) {
|
||||||
return true;
|
const keyvals = [];
|
||||||
|
for (const unicode of string) {
|
||||||
|
const keyval = Clutter.unicode_to_keysym(unicode.codePointAt(0));
|
||||||
|
// If the unicode character is unknown, try to avoid keyvals at all
|
||||||
|
if (keyval === (unicode || 0x01000000))
|
||||||
|
return [];
|
||||||
|
|
||||||
|
keyvals.push(keyval);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyvals;
|
||||||
|
}
|
||||||
|
|
||||||
|
async commit(str, modifiers) {
|
||||||
|
const keyvals = this._getKeyvalsFromString(str);
|
||||||
|
|
||||||
|
// If there is no IM focus (e.g. with X11 clients), or modifiers
|
||||||
|
// are in use, send raw key events.
|
||||||
|
if (!Main.inputMethod.currentFocus || modifiers?.size > 0) {
|
||||||
|
if (modifiers)
|
||||||
|
this._forwardModifiers(modifiers, Clutter.EventType.KEY_PRESS);
|
||||||
|
|
||||||
|
for (const keyval of keyvals) {
|
||||||
|
this.keyvalPress(keyval);
|
||||||
|
this.keyvalRelease(keyval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modifiers)
|
||||||
|
this._forwardModifiers(modifiers, Clutter.EventType.KEY_RELEASE);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If OSK completion is enabled, or there is an active source requiring
|
||||||
|
// IBus to receive input, prefer to feed the events directly to the IM
|
||||||
|
if (this._oskCompletionEnabled ||
|
||||||
|
this._currentSource.type === InputSourceManager.INPUT_SOURCE_TYPE_IBUS) {
|
||||||
|
for (const keyval of keyvals) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
if (!await Main.inputMethod.handleVirtualKey(keyval)) {
|
||||||
|
this.keyvalPress(keyval);
|
||||||
|
this.keyvalRelease(keyval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Main.inputMethod.commit(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
set oskCompletion(enabled) {
|
||||||
|
if (this._oskCompletionEnabled === enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._oskCompletionEnabled =
|
||||||
|
IBusManager.getIBusManager().setCompletionEnabled(enabled, () => Main.inputMethod.update());
|
||||||
}
|
}
|
||||||
|
|
||||||
keyvalPress(keyval) {
|
keyvalPress(keyval) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user