2011-09-28 13:16:26 +00:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2019-08-28 21:52:52 +00:00
|
|
|
/* exported KeyboardManager */
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2022-06-15 13:49:19 +00:00
|
|
|
const {Clutter, Gio, GLib, GObject, Graphene, IBus, Meta, Shell, St} = imports.gi;
|
2022-07-04 22:30:44 +00:00
|
|
|
const Signals = imports.misc.signals;
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2021-02-11 12:41:16 +00:00
|
|
|
const EdgeDragAction = imports.ui.edgeDragAction;
|
2019-02-09 03:21:36 +00:00
|
|
|
const InputSourceManager = imports.ui.status.keyboard;
|
2018-05-25 09:35:49 +00:00
|
|
|
const IBusManager = imports.misc.ibusManager;
|
2011-08-29 15:11:22 +00:00
|
|
|
const BoxPointer = imports.ui.boxpointer;
|
|
|
|
const Main = imports.ui.main;
|
2017-12-22 15:02:51 +00:00
|
|
|
const PageIndicators = imports.ui.pageIndicators;
|
2018-01-23 15:45:46 +00:00
|
|
|
const PopupMenu = imports.ui.popupMenu;
|
2022-06-28 21:47:04 +00:00
|
|
|
const SwipeTracker = imports.ui.swipeTracker;
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2021-02-11 12:08:47 +00:00
|
|
|
var KEYBOARD_ANIMATION_TIME = 150;
|
|
|
|
var KEYBOARD_REST_TIME = KEYBOARD_ANIMATION_TIME * 2;
|
2017-08-05 10:21:30 +00:00
|
|
|
var KEY_LONG_PRESS_TIME = 250;
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2011-08-29 17:02:42 +00:00
|
|
|
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
|
|
|
|
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
|
2022-06-29 18:26:27 +00:00
|
|
|
const EMOJI_PAGE_SEPARATION = 32;
|
2011-08-29 17:02:42 +00:00
|
|
|
|
2017-12-20 12:08:50 +00:00
|
|
|
/* KeyContainer puts keys in a grid where a 1:1 key takes this size */
|
|
|
|
const KEY_SIZE = 2;
|
|
|
|
|
2022-04-16 11:06:09 +00:00
|
|
|
const KEY_RELEASE_TIMEOUT = 50;
|
2022-04-20 21:18:27 +00:00
|
|
|
const BACKSPACE_WORD_DELETE_THRESHOLD = 50;
|
2022-04-16 11:06:09 +00:00
|
|
|
|
2019-01-21 20:39:27 +00:00
|
|
|
var AspectContainer = GObject.registerClass(
|
|
|
|
class AspectContainer extends St.Widget {
|
|
|
|
_init(params) {
|
|
|
|
super._init(params);
|
|
|
|
this._ratio = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
setRatio(relWidth, relHeight) {
|
|
|
|
this._ratio = relWidth / relHeight;
|
|
|
|
this.queue_relayout();
|
|
|
|
}
|
|
|
|
|
2020-07-04 00:36:10 +00:00
|
|
|
vfunc_get_preferred_width(forHeight) {
|
|
|
|
let [min, nat] = super.vfunc_get_preferred_width(forHeight);
|
|
|
|
|
|
|
|
if (forHeight > 0)
|
|
|
|
nat = forHeight * this._ratio;
|
|
|
|
|
|
|
|
return [min, nat];
|
|
|
|
}
|
|
|
|
|
|
|
|
vfunc_get_preferred_height(forWidth) {
|
|
|
|
let [min, nat] = super.vfunc_get_preferred_height(forWidth);
|
|
|
|
|
|
|
|
if (forWidth > 0)
|
|
|
|
nat = forWidth / this._ratio;
|
|
|
|
|
|
|
|
return [min, nat];
|
|
|
|
}
|
|
|
|
|
2020-05-09 19:30:26 +00:00
|
|
|
vfunc_allocate(box) {
|
2019-01-21 20:39:27 +00:00
|
|
|
if (box.get_width() > 0 && box.get_height() > 0) {
|
|
|
|
let sizeRatio = box.get_width() / box.get_height();
|
|
|
|
if (sizeRatio >= this._ratio) {
|
|
|
|
/* Restrict horizontally */
|
|
|
|
let width = box.get_height() * this._ratio;
|
|
|
|
let diff = box.get_width() - width;
|
|
|
|
|
|
|
|
box.x1 += Math.floor(diff / 2);
|
|
|
|
box.x2 -= Math.ceil(diff / 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-09 19:30:26 +00:00
|
|
|
super.vfunc_allocate(box);
|
2019-01-21 20:39:27 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-01-31 19:38:24 +00:00
|
|
|
var KeyContainer = GObject.registerClass(
|
2017-10-31 01:23:39 +00:00
|
|
|
class KeyContainer extends St.Widget {
|
2017-10-31 00:03:21 +00:00
|
|
|
_init() {
|
2020-03-29 21:51:13 +00:00
|
|
|
const gridLayout = new Clutter.GridLayout({
|
|
|
|
orientation: Clutter.Orientation.HORIZONTAL,
|
|
|
|
column_homogeneous: true,
|
|
|
|
row_homogeneous: true,
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
super._init({
|
|
|
|
layout_manager: gridLayout,
|
|
|
|
x_expand: true,
|
2019-08-20 21:43:54 +00:00
|
|
|
y_expand: true,
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2017-12-20 12:08:50 +00:00
|
|
|
this._gridLayout = gridLayout;
|
|
|
|
this._currentRow = 0;
|
|
|
|
this._currentCol = 0;
|
|
|
|
this._maxCols = 0;
|
|
|
|
|
|
|
|
this._currentRow = null;
|
|
|
|
this._rows = [];
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2017-12-20 12:08:50 +00:00
|
|
|
|
2019-02-04 11:30:53 +00:00
|
|
|
appendRow() {
|
2017-12-20 12:08:50 +00:00
|
|
|
this._currentRow++;
|
|
|
|
this._currentCol = 0;
|
|
|
|
|
2019-08-19 19:18:54 +00:00
|
|
|
let row = {
|
|
|
|
keys: [],
|
|
|
|
width: 0,
|
|
|
|
};
|
2017-12-20 12:08:50 +00:00
|
|
|
this._rows.push(row);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2017-12-20 12:08:50 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
appendKey(key, width = 1, height = 1) {
|
2017-12-20 12:08:50 +00:00
|
|
|
let keyInfo = {
|
|
|
|
key,
|
|
|
|
left: this._currentCol,
|
|
|
|
top: this._currentRow,
|
|
|
|
width,
|
2019-08-20 21:43:54 +00:00
|
|
|
height,
|
2017-12-20 12:08:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let row = this._rows[this._rows.length - 1];
|
|
|
|
row.keys.push(keyInfo);
|
|
|
|
row.width += width;
|
|
|
|
|
|
|
|
this._currentCol += width;
|
|
|
|
this._maxCols = Math.max(this._currentCol, this._maxCols);
|
2017-10-31 01:23:39 +00:00
|
|
|
}
|
2017-12-20 12:08:50 +00:00
|
|
|
|
2022-05-30 21:12:51 +00:00
|
|
|
layoutButtons() {
|
2017-12-20 12:08:50 +00:00
|
|
|
let nCol = 0, nRow = 0;
|
|
|
|
|
|
|
|
for (let i = 0; i < this._rows.length; i++) {
|
|
|
|
let row = this._rows[i];
|
|
|
|
|
|
|
|
/* When starting a new row, see if we need some padding */
|
|
|
|
if (nCol == 0) {
|
|
|
|
let diff = this._maxCols - row.width;
|
|
|
|
if (diff >= 1)
|
|
|
|
nCol = diff * KEY_SIZE / 2;
|
|
|
|
else
|
|
|
|
nCol = diff * KEY_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let j = 0; j < row.keys.length; j++) {
|
|
|
|
let keyInfo = row.keys[j];
|
|
|
|
let width = keyInfo.width * KEY_SIZE;
|
|
|
|
let height = keyInfo.height * KEY_SIZE;
|
|
|
|
|
|
|
|
this._gridLayout.attach(keyInfo.key, nCol, nRow, width, height);
|
|
|
|
nCol += width;
|
|
|
|
}
|
|
|
|
|
|
|
|
nRow += KEY_SIZE;
|
|
|
|
nCol = 0;
|
|
|
|
}
|
2022-05-30 21:12:51 +00:00
|
|
|
}
|
2019-01-21 20:39:27 +00:00
|
|
|
|
2022-05-30 21:12:51 +00:00
|
|
|
getRatio() {
|
|
|
|
return [this._maxCols, this._rows.length];
|
2017-12-20 12:08:50 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
var Suggestions = GObject.registerClass(
|
|
|
|
class Suggestions extends St.BoxLayout {
|
|
|
|
_init() {
|
2019-10-21 18:44:00 +00:00
|
|
|
super._init({
|
|
|
|
style_class: 'word-suggestions',
|
|
|
|
vertical: false,
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
this.show();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-06 12:46:02 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
add(word, callback) {
|
2017-12-06 12:46:02 +00:00
|
|
|
let button = new St.Button({ label: word });
|
2022-04-22 14:19:14 +00:00
|
|
|
button.connect('button-press-event', () => {
|
|
|
|
callback();
|
|
|
|
return Clutter.EVENT_STOP;
|
|
|
|
});
|
|
|
|
button.connect('touch-event', (actor, event) => {
|
|
|
|
if (event.type() !== Clutter.EventType.TOUCH_BEGIN)
|
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
|
|
|
|
callback();
|
|
|
|
return Clutter.EVENT_STOP;
|
|
|
|
});
|
2021-04-13 02:07:54 +00:00
|
|
|
this.add_child(button);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-06 12:46:02 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
clear() {
|
2019-07-16 09:24:13 +00:00
|
|
|
this.remove_all_children();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2022-06-29 11:06:07 +00:00
|
|
|
|
|
|
|
setVisible(visible) {
|
|
|
|
for (const child of this)
|
|
|
|
child.visible = visible;
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2017-12-06 12:46:02 +00:00
|
|
|
|
2017-10-31 01:19:44 +00:00
|
|
|
var LanguageSelectionPopup = class extends PopupMenu.PopupMenu {
|
|
|
|
constructor(actor) {
|
|
|
|
super(actor, 0.5, St.Side.BOTTOM);
|
2018-01-23 15:45:46 +00:00
|
|
|
|
|
|
|
let inputSourceManager = InputSourceManager.getInputSourceManager();
|
|
|
|
let inputSources = inputSourceManager.inputSources;
|
|
|
|
|
2018-09-11 16:00:22 +00:00
|
|
|
let item;
|
2018-01-23 15:45:46 +00:00
|
|
|
for (let i in inputSources) {
|
|
|
|
let is = inputSources[i];
|
|
|
|
|
2018-09-11 16:00:22 +00:00
|
|
|
item = this.addAction(is.displayName, () => {
|
2018-01-23 15:45:46 +00:00
|
|
|
inputSourceManager.activateInputSource(is, true);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2019-07-12 23:50:58 +00:00
|
|
|
item.can_focus = false;
|
2022-06-27 11:55:34 +00:00
|
|
|
item.setOrnament(is === inputSourceManager.currentSource
|
|
|
|
? PopupMenu.Ornament.DOT
|
|
|
|
: PopupMenu.Ornament.NONE);
|
2018-01-23 15:45:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
2018-09-11 16:00:22 +00:00
|
|
|
item = this.addSettingsAction(_("Region & Language Settings"), 'gnome-region-panel.desktop');
|
2019-07-12 23:50:58 +00:00
|
|
|
item.can_focus = false;
|
2018-09-11 16:00:22 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
actor.connectObject('notify::mapped', () => {
|
2018-01-23 15:45:46 +00:00
|
|
|
if (!actor.is_mapped())
|
|
|
|
this.close(true);
|
2021-08-15 22:36:59 +00:00
|
|
|
}, this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-01-23 15:45:46 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onCapturedEvent(actor, event) {
|
2022-02-25 12:09:17 +00:00
|
|
|
const targetActor = global.stage.get_event_actor(event);
|
|
|
|
|
|
|
|
if (targetActor === this.actor ||
|
|
|
|
this.actor.contains(targetActor))
|
2018-01-23 15:45:46 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
|
|
|
|
if (event.type() == Clutter.EventType.BUTTON_RELEASE || event.type() == Clutter.EventType.TOUCH_END)
|
|
|
|
this.close(true);
|
|
|
|
|
|
|
|
return Clutter.EVENT_STOP;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-01-23 15:45:46 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
open(animate) {
|
2017-10-31 01:19:44 +00:00
|
|
|
super.open(animate);
|
2021-08-15 22:36:59 +00:00
|
|
|
global.stage.connectObject(
|
|
|
|
'captured-event', this._onCapturedEvent.bind(this), this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-01-23 15:45:46 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
close(animate) {
|
2017-10-31 01:19:44 +00:00
|
|
|
super.close(animate);
|
2021-08-15 22:36:59 +00:00
|
|
|
global.stage.disconnectObject(this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-01-23 15:45:46 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
destroy() {
|
2021-08-15 22:36:59 +00:00
|
|
|
global.stage.disconnectObject(this);
|
|
|
|
this.sourceActor.disconnectObject(this);
|
2017-10-31 01:19:44 +00:00
|
|
|
super.destroy();
|
|
|
|
}
|
|
|
|
};
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
var Key = GObject.registerClass({
|
|
|
|
Signals: {
|
|
|
|
'long-press': {},
|
2022-04-14 18:42:30 +00:00
|
|
|
'pressed': {},
|
|
|
|
'released': {},
|
|
|
|
'commit': {param_types: [GObject.TYPE_UINT, GObject.TYPE_STRING]},
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class Key extends St.BoxLayout {
|
2022-04-14 12:46:03 +00:00
|
|
|
_init(params, extendedKeys = []) {
|
|
|
|
const {label, iconName, commitString, keyval} = {keyval: 0, ...params};
|
2019-07-16 09:24:13 +00:00
|
|
|
super._init({ style_class: 'key-container' });
|
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
this._keyval = parseInt(keyval, 16);
|
|
|
|
this.keyButton = this._makeKey(commitString, label, iconName);
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-08-05 10:21:30 +00:00
|
|
|
/* Add the key in a container, so keys can be padded without losing
|
|
|
|
* logical proportions between those.
|
|
|
|
*/
|
2019-10-21 18:44:00 +00:00
|
|
|
this.add_child(this.keyButton);
|
2019-07-16 09:24:13 +00:00
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2019-12-04 17:51:08 +00:00
|
|
|
this._extendedKeys = extendedKeys;
|
|
|
|
this._extendedKeyboard = null;
|
2017-08-05 10:21:30 +00:00
|
|
|
this._pressTimeoutId = 0;
|
2017-12-22 14:59:19 +00:00
|
|
|
this._capturedPress = false;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2022-04-16 11:06:09 +00:00
|
|
|
get iconName() {
|
|
|
|
return this._icon.icon_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
set iconName(value) {
|
|
|
|
this._icon.icon_name = value;
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDestroy() {
|
2014-09-20 00:44:45 +00:00
|
|
|
if (this._boxPointer) {
|
2018-08-21 10:38:23 +00:00
|
|
|
this._boxPointer.destroy();
|
2014-09-20 00:44:45 +00:00
|
|
|
this._boxPointer = null;
|
|
|
|
}
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
this.cancel();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-09-20 00:44:45 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_ensureExtendedKeysPopup() {
|
2019-12-04 17:51:08 +00:00
|
|
|
if (this._extendedKeys.length === 0)
|
2017-08-05 10:21:30 +00:00
|
|
|
return;
|
|
|
|
|
2019-12-04 18:02:33 +00:00
|
|
|
if (this._boxPointer)
|
|
|
|
return;
|
|
|
|
|
2019-10-17 21:40:24 +00:00
|
|
|
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM);
|
2018-08-21 10:38:23 +00:00
|
|
|
this._boxPointer.hide();
|
2019-06-18 22:24:01 +00:00
|
|
|
Main.layoutManager.addTopChrome(this._boxPointer);
|
2018-02-11 15:19:10 +00:00
|
|
|
this._boxPointer.setPosition(this.keyButton, 0.5);
|
2017-08-05 10:21:30 +00:00
|
|
|
|
|
|
|
// Adds style to existing keyboard style to avoid repetition
|
2019-04-09 20:21:15 +00:00
|
|
|
this._boxPointer.add_style_class_name('keyboard-subkeys');
|
2017-08-05 10:21:30 +00:00
|
|
|
this._getExtendedKeys();
|
2019-12-04 17:51:08 +00:00
|
|
|
this.keyButton._extendedKeys = this._extendedKeyboard;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
_getKeyvalFromString(string) {
|
|
|
|
let unicode = string?.length ? string.charCodeAt(0) : undefined;
|
2018-11-26 12:39:57 +00:00
|
|
|
return Clutter.unicode_to_keysym(unicode);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2022-04-14 18:42:30 +00:00
|
|
|
_press(button) {
|
2022-04-14 12:46:03 +00:00
|
|
|
if (button === this.keyButton) {
|
2017-08-05 10:21:30 +00:00
|
|
|
this._pressTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
2019-12-04 19:40:07 +00:00
|
|
|
KEY_LONG_PRESS_TIME,
|
|
|
|
() => {
|
|
|
|
this._pressTimeoutId = 0;
|
|
|
|
|
|
|
|
this.emit('long-press');
|
|
|
|
|
2019-12-04 17:51:08 +00:00
|
|
|
if (this._extendedKeys.length > 0) {
|
2021-03-22 17:01:39 +00:00
|
|
|
this._touchPressSlot = null;
|
2019-12-04 19:01:41 +00:00
|
|
|
this._ensureExtendedKeysPopup();
|
2019-12-04 19:40:07 +00:00
|
|
|
this.keyButton.set_hover(false);
|
|
|
|
this.keyButton.fake_release();
|
|
|
|
this._showSubkeys();
|
|
|
|
}
|
|
|
|
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
});
|
2017-08-05 10:21:30 +00:00
|
|
|
}
|
2022-04-14 12:46:03 +00:00
|
|
|
|
2022-04-14 18:42:30 +00:00
|
|
|
this.emit('pressed');
|
|
|
|
this._pressed = true;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
_release(button, commitString) {
|
2017-08-05 10:21:30 +00:00
|
|
|
if (this._pressTimeoutId != 0) {
|
|
|
|
GLib.source_remove(this._pressTimeoutId);
|
|
|
|
this._pressTimeoutId = 0;
|
|
|
|
}
|
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
let keyval;
|
|
|
|
if (button === this.keyButton)
|
|
|
|
keyval = this._keyval;
|
|
|
|
if (!keyval && commitString)
|
|
|
|
keyval = this._getKeyvalFromString(commitString);
|
|
|
|
console.assert(keyval !== undefined, 'Need keyval or commitString');
|
|
|
|
|
2022-04-14 18:42:30 +00:00
|
|
|
if (this._pressed && (commitString || keyval))
|
|
|
|
this.emit('commit', keyval, commitString || '');
|
2018-02-15 11:18:59 +00:00
|
|
|
|
2022-04-14 18:42:30 +00:00
|
|
|
this.emit('released');
|
2017-12-22 14:59:19 +00:00
|
|
|
this._hideSubkeys();
|
2022-04-14 18:42:30 +00:00
|
|
|
this._pressed = false;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-22 14:59:19 +00:00
|
|
|
|
2017-12-22 15:02:51 +00:00
|
|
|
cancel() {
|
|
|
|
if (this._pressTimeoutId != 0) {
|
|
|
|
GLib.source_remove(this._pressTimeoutId);
|
|
|
|
this._pressTimeoutId = 0;
|
|
|
|
}
|
2021-03-22 17:01:39 +00:00
|
|
|
this._touchPressSlot = null;
|
2017-12-22 15:02:51 +00:00
|
|
|
this.keyButton.set_hover(false);
|
|
|
|
this.keyButton.fake_release();
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onCapturedEvent(actor, event) {
|
2017-12-22 14:59:19 +00:00
|
|
|
let type = event.type();
|
2019-08-19 19:38:51 +00:00
|
|
|
let press = type == Clutter.EventType.BUTTON_PRESS || type == Clutter.EventType.TOUCH_BEGIN;
|
|
|
|
let release = type == Clutter.EventType.BUTTON_RELEASE || type == Clutter.EventType.TOUCH_END;
|
2022-02-25 12:09:17 +00:00
|
|
|
const targetActor = global.stage.get_event_actor(event);
|
2017-12-22 14:59:19 +00:00
|
|
|
|
2022-02-25 12:09:17 +00:00
|
|
|
if (targetActor === this._boxPointer.bin ||
|
|
|
|
this._boxPointer.bin.contains(targetActor))
|
2017-12-22 14:59:19 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
|
|
|
|
|
|
|
if (press)
|
|
|
|
this._capturedPress = true;
|
|
|
|
else if (release && this._capturedPress)
|
|
|
|
this._hideSubkeys();
|
|
|
|
|
|
|
|
return Clutter.EVENT_STOP;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-22 14:59:19 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_showSubkeys() {
|
2018-08-21 10:29:44 +00:00
|
|
|
this._boxPointer.open(BoxPointer.PopupAnimation.FULL);
|
2021-08-15 22:36:59 +00:00
|
|
|
global.stage.connectObject(
|
|
|
|
'captured-event', this._onCapturedEvent.bind(this), this);
|
|
|
|
this.keyButton.connectObject('notify::mapped', () => {
|
2018-02-11 15:19:10 +00:00
|
|
|
if (!this.keyButton.is_mapped())
|
2017-12-22 14:59:19 +00:00
|
|
|
this._hideSubkeys();
|
2021-08-15 22:36:59 +00:00
|
|
|
}, this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-22 14:59:19 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_hideSubkeys() {
|
2017-12-22 14:59:19 +00:00
|
|
|
if (this._boxPointer)
|
2018-08-21 10:29:44 +00:00
|
|
|
this._boxPointer.close(BoxPointer.PopupAnimation.FULL);
|
2021-08-15 22:36:59 +00:00
|
|
|
global.stage.disconnectObject(this);
|
|
|
|
this.keyButton.disconnectObject(this);
|
2017-12-22 14:59:19 +00:00
|
|
|
this._capturedPress = false;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
_makeKey(commitString, label, icon) {
|
2019-08-19 17:55:49 +00:00
|
|
|
let button = new St.Button({
|
2019-10-21 18:44:00 +00:00
|
|
|
style_class: 'keyboard-key',
|
|
|
|
x_expand: true,
|
|
|
|
});
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2020-02-14 17:09:32 +00:00
|
|
|
if (icon) {
|
2022-04-14 12:46:03 +00:00
|
|
|
const child = new St.Icon({icon_name: icon});
|
2020-02-14 17:09:32 +00:00
|
|
|
button.set_child(child);
|
|
|
|
this._icon = child;
|
2022-04-14 12:46:03 +00:00
|
|
|
} else if (label) {
|
2020-02-14 17:09:32 +00:00
|
|
|
button.set_label(label);
|
2022-04-14 12:46:03 +00:00
|
|
|
} else if (commitString) {
|
|
|
|
const str = GLib.markup_escape_text(commitString, -1);
|
|
|
|
button.set_label(str);
|
2020-02-14 17:09:32 +00:00
|
|
|
}
|
|
|
|
|
2017-08-05 10:21:30 +00:00
|
|
|
button.keyWidth = 1;
|
2017-10-31 00:38:18 +00:00
|
|
|
button.connect('button-press-event', () => {
|
2022-04-14 12:46:03 +00:00
|
|
|
this._press(button, commitString);
|
2022-03-09 22:32:30 +00:00
|
|
|
button.add_style_pseudo_class('active');
|
|
|
|
return Clutter.EVENT_STOP;
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
|
|
|
button.connect('button-release-event', () => {
|
2022-04-14 12:46:03 +00:00
|
|
|
this._release(button, commitString);
|
2022-03-09 22:32:30 +00:00
|
|
|
button.remove_style_pseudo_class('active');
|
|
|
|
return Clutter.EVENT_STOP;
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
|
|
|
button.connect('touch-event', (actor, event) => {
|
|
|
|
// We only handle touch events here on wayland. On X11
|
|
|
|
// we do get emulated pointer events, which already works
|
|
|
|
// for single-touch cases. Besides, the X11 passive touch grab
|
|
|
|
// set up by Mutter will make us see first the touch events
|
|
|
|
// and later the pointer events, so it will look like two
|
|
|
|
// unrelated series of events, we want to avoid double handling
|
|
|
|
// in these cases.
|
|
|
|
if (!Meta.is_wayland_compositor())
|
2013-11-29 18:17:34 +00:00
|
|
|
return Clutter.EVENT_PROPAGATE;
|
2017-10-31 00:38:18 +00:00
|
|
|
|
2021-03-22 17:01:39 +00:00
|
|
|
const slot = event.get_event_sequence().get_slot();
|
|
|
|
|
|
|
|
if (!this._touchPressSlot &&
|
2017-10-31 00:38:18 +00:00
|
|
|
event.type() == Clutter.EventType.TOUCH_BEGIN) {
|
2021-03-22 17:01:39 +00:00
|
|
|
this._touchPressSlot = slot;
|
2022-04-14 12:46:03 +00:00
|
|
|
this._press(button, commitString);
|
2022-03-09 22:32:30 +00:00
|
|
|
button.add_style_pseudo_class('active');
|
2021-03-22 17:01:39 +00:00
|
|
|
} else if (event.type() === Clutter.EventType.TOUCH_END) {
|
|
|
|
if (!this._touchPressSlot ||
|
2022-03-09 22:32:30 +00:00
|
|
|
this._touchPressSlot === slot) {
|
2022-04-14 12:46:03 +00:00
|
|
|
this._release(button, commitString);
|
2022-03-09 22:32:30 +00:00
|
|
|
button.remove_style_pseudo_class('active');
|
|
|
|
}
|
2021-03-22 17:01:39 +00:00
|
|
|
|
|
|
|
if (this._touchPressSlot === slot)
|
|
|
|
this._touchPressSlot = null;
|
2017-10-31 00:38:18 +00:00
|
|
|
}
|
2022-03-09 22:32:30 +00:00
|
|
|
return Clutter.EVENT_STOP;
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2011-08-29 15:11:22 +00:00
|
|
|
|
|
|
|
return button;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_getExtendedKeys() {
|
2019-12-04 17:51:08 +00:00
|
|
|
this._extendedKeyboard = new St.BoxLayout({
|
|
|
|
style_class: 'key-container',
|
|
|
|
vertical: false,
|
|
|
|
});
|
|
|
|
for (let i = 0; i < this._extendedKeys.length; ++i) {
|
|
|
|
let extendedKey = this._extendedKeys[i];
|
2017-08-05 10:21:30 +00:00
|
|
|
let key = this._makeKey(extendedKey);
|
2014-07-22 10:38:44 +00:00
|
|
|
|
2019-12-04 17:51:08 +00:00
|
|
|
key.extendedKey = extendedKey;
|
|
|
|
this._extendedKeyboard.add(key);
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2019-12-04 18:48:36 +00:00
|
|
|
key.set_size(...this.keyButton.allocation.get_size());
|
2019-12-04 18:49:17 +00:00
|
|
|
this.keyButton.connect('notify::allocation',
|
|
|
|
() => key.set_size(...this.keyButton.allocation.get_size()));
|
2011-08-29 15:11:22 +00:00
|
|
|
}
|
2019-12-04 17:51:08 +00:00
|
|
|
this._boxPointer.bin.add_actor(this._extendedKeyboard);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2012-11-10 21:47:57 +00:00
|
|
|
get subkeys() {
|
|
|
|
return this._boxPointer;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
setWidth(width) {
|
2018-02-11 15:19:10 +00:00
|
|
|
this.keyButton.keyWidth = width;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-02-15 13:48:46 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
setLatched(latched) {
|
2022-04-16 11:06:09 +00:00
|
|
|
if (latched)
|
2018-02-15 13:48:46 +00:00
|
|
|
this.keyButton.add_style_pseudo_class('latched');
|
2022-04-16 11:06:09 +00:00
|
|
|
else
|
2018-02-15 13:48:46 +00:00
|
|
|
this.keyButton.remove_style_pseudo_class('latched');
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2017-10-31 01:19:44 +00:00
|
|
|
var KeyboardModel = class {
|
|
|
|
constructor(groupName) {
|
2019-11-16 12:48:35 +00:00
|
|
|
let names = [groupName];
|
2020-03-23 15:14:47 +00:00
|
|
|
if (groupName.includes('+'))
|
2019-11-16 12:48:35 +00:00
|
|
|
names.push(groupName.replace(/\+.*/, ''));
|
|
|
|
names.push('us');
|
|
|
|
|
|
|
|
for (let i = 0; i < names.length; i++) {
|
|
|
|
try {
|
|
|
|
this._model = this._loadModel(names[i]);
|
|
|
|
break;
|
|
|
|
} catch (e) {
|
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_loadModel(groupName) {
|
2022-02-07 14:14:06 +00:00
|
|
|
const file = Gio.File.new_for_uri(
|
|
|
|
`resource:///org/gnome/shell/osk-layouts/${groupName}.json`);
|
2019-01-31 14:08:00 +00:00
|
|
|
let [success_, contents] = file.load_contents(null);
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2021-08-12 14:38:57 +00:00
|
|
|
const decoder = new TextDecoder();
|
|
|
|
return JSON.parse(decoder.decode(contents));
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
getLevels() {
|
2017-08-05 10:21:30 +00:00
|
|
|
return this._model.levels;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
getKeysForLevel(levelName) {
|
2017-08-05 10:21:30 +00:00
|
|
|
return this._model.levels.find(level => level == levelName);
|
2011-08-29 15:11:22 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
};
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2022-07-04 22:30:44 +00:00
|
|
|
var FocusTracker = class extends Signals.EventEmitter {
|
2017-10-31 01:19:44 +00:00
|
|
|
constructor() {
|
2022-07-04 22:30:44 +00:00
|
|
|
super();
|
|
|
|
|
2018-09-17 19:04:48 +00:00
|
|
|
this._rect = null;
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
global.display.connectObject(
|
|
|
|
'notify::focus-window', () => {
|
|
|
|
this._setCurrentWindow(global.display.focus_window);
|
|
|
|
this.emit('window-changed', this._currentWindow);
|
|
|
|
},
|
|
|
|
'grab-op-begin', (display, window, op) => {
|
|
|
|
if (window === this._currentWindow &&
|
|
|
|
(op === Meta.GrabOp.MOVING || op === Meta.GrabOp.KEYBOARD_MOVING))
|
|
|
|
this.emit('window-grabbed');
|
|
|
|
}, this);
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2021-02-26 14:52:06 +00:00
|
|
|
this._setCurrentWindow(global.display.focus_window);
|
|
|
|
|
2018-05-25 09:35:49 +00:00
|
|
|
/* Valid for wayland clients */
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.inputMethod.connectObject('cursor-location-changed',
|
|
|
|
(o, rect) => this._setCurrentRect(rect), this);
|
2018-05-25 09:35:49 +00:00
|
|
|
|
|
|
|
this._ibusManager = IBusManager.getIBusManager();
|
2021-08-15 22:36:59 +00:00
|
|
|
this._ibusManager.connectObject(
|
|
|
|
'set-cursor-location', (manager, rect) => {
|
2021-02-26 13:34:36 +00:00
|
|
|
/* Valid for X11 clients only */
|
|
|
|
if (Main.inputMethod.currentFocus)
|
|
|
|
return;
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2021-02-27 10:26:00 +00:00
|
|
|
const grapheneRect = new Graphene.Rect();
|
|
|
|
grapheneRect.init(rect.x, rect.y, rect.width, rect.height);
|
|
|
|
|
|
|
|
this._setCurrentRect(grapheneRect);
|
2021-08-15 22:36:59 +00:00
|
|
|
},
|
|
|
|
'focus-in', () => this.emit('focus-changed', true),
|
|
|
|
'focus-out', () => this.emit('focus-changed', false),
|
|
|
|
this);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2021-02-26 13:34:36 +00:00
|
|
|
destroy() {
|
2021-08-15 22:36:59 +00:00
|
|
|
this._currentWindow?.disconnectObject(this);
|
|
|
|
global.display.disconnectObject(this);
|
|
|
|
Main.inputMethod.disconnectObject(this);
|
|
|
|
this._ibusManager.disconnectObject(this);
|
2021-02-26 13:34:36 +00:00
|
|
|
}
|
|
|
|
|
2018-05-25 09:35:49 +00:00
|
|
|
get currentWindow() {
|
|
|
|
return this._currentWindow;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-05-25 09:35:49 +00:00
|
|
|
|
|
|
|
_setCurrentWindow(window) {
|
2021-08-15 22:36:59 +00:00
|
|
|
this._currentWindow?.disconnectObject(this);
|
2021-02-26 15:08:20 +00:00
|
|
|
|
2018-05-25 09:35:49 +00:00
|
|
|
this._currentWindow = window;
|
2021-02-26 15:08:20 +00:00
|
|
|
|
|
|
|
if (this._currentWindow) {
|
2021-08-15 22:36:59 +00:00
|
|
|
this._currentWindow.connectObject(
|
|
|
|
'position-changed', () => this.emit('window-moved'), this);
|
2021-02-26 15:08:20 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-05-25 09:35:49 +00:00
|
|
|
|
|
|
|
_setCurrentRect(rect) {
|
2021-02-27 10:27:23 +00:00
|
|
|
// Some clients give us 0-sized rects, in that case set size to 1
|
|
|
|
if (rect.size.width <= 0)
|
|
|
|
rect.size.width = 1;
|
|
|
|
if (rect.size.height <= 0)
|
|
|
|
rect.size.height = 1;
|
|
|
|
|
2018-07-16 23:36:38 +00:00
|
|
|
if (this._currentWindow) {
|
2021-02-27 10:26:00 +00:00
|
|
|
const frameRect = this._currentWindow.get_frame_rect();
|
|
|
|
const grapheneFrameRect = new Graphene.Rect();
|
|
|
|
grapheneFrameRect.init(frameRect.x, frameRect.y,
|
|
|
|
frameRect.width, frameRect.height);
|
|
|
|
|
2021-02-27 10:27:23 +00:00
|
|
|
const rectInsideFrameRect = grapheneFrameRect.intersection(rect)[0];
|
|
|
|
if (!rectInsideFrameRect)
|
|
|
|
return;
|
2018-07-16 23:36:38 +00:00
|
|
|
}
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2021-02-27 10:26:00 +00:00
|
|
|
if (this._rect && this._rect.equal(rect))
|
2018-09-17 19:04:48 +00:00
|
|
|
return;
|
|
|
|
|
2018-05-25 09:35:49 +00:00
|
|
|
this._rect = rect;
|
|
|
|
this.emit('position-changed');
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-05-25 09:35:49 +00:00
|
|
|
|
|
|
|
getCurrentRect() {
|
2021-02-27 10:26:00 +00:00
|
|
|
const rect = {
|
|
|
|
x: this._rect.origin.x,
|
|
|
|
y: this._rect.origin.y,
|
|
|
|
width: this._rect.size.width,
|
|
|
|
height: this._rect.size.height,
|
|
|
|
};
|
2018-07-16 23:36:38 +00:00
|
|
|
|
2018-05-25 09:35:49 +00:00
|
|
|
return rect;
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
};
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2019-07-25 16:53:00 +00:00
|
|
|
var EmojiPager = GObject.registerClass({
|
2019-07-25 22:13:19 +00:00
|
|
|
Properties: {
|
|
|
|
'delta': GObject.ParamSpec.int(
|
|
|
|
'delta', 'delta', 'delta',
|
|
|
|
GObject.ParamFlags.READWRITE,
|
2019-08-20 21:43:54 +00:00
|
|
|
GLib.MININT32, GLib.MAXINT32, 0),
|
2019-07-25 22:13:19 +00:00
|
|
|
},
|
2019-07-25 16:53:00 +00:00
|
|
|
Signals: {
|
|
|
|
'emoji': { param_types: [GObject.TYPE_STRING] },
|
|
|
|
'page-changed': {
|
2019-08-20 21:43:54 +00:00
|
|
|
param_types: [GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_INT],
|
|
|
|
},
|
|
|
|
},
|
2019-07-25 16:53:00 +00:00
|
|
|
}, class EmojiPager extends St.Widget {
|
2022-06-28 16:53:56 +00:00
|
|
|
_init(sections) {
|
2019-07-25 16:53:00 +00:00
|
|
|
super._init({
|
|
|
|
layout_manager: new Clutter.BinLayout(),
|
|
|
|
reactive: true,
|
2019-08-20 21:43:54 +00:00
|
|
|
clip_to_allocation: true,
|
2020-01-21 15:22:34 +00:00
|
|
|
y_expand: true,
|
2019-07-25 16:53:00 +00:00
|
|
|
});
|
2017-12-22 15:02:51 +00:00
|
|
|
this._sections = sections;
|
|
|
|
|
|
|
|
this._pages = [];
|
|
|
|
this._panel = null;
|
|
|
|
this._curPage = null;
|
|
|
|
this._followingPage = null;
|
|
|
|
this._followingPanel = null;
|
|
|
|
this._currentKey = null;
|
|
|
|
this._delta = 0;
|
|
|
|
this._width = null;
|
|
|
|
|
2022-06-28 21:47:04 +00:00
|
|
|
const swipeTracker = new SwipeTracker.SwipeTracker(this,
|
|
|
|
Clutter.Orientation.HORIZONTAL,
|
|
|
|
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
|
|
|
|
{allowDrag: true, allowScroll: true});
|
|
|
|
swipeTracker.connect('begin', this._onSwipeBegin.bind(this));
|
|
|
|
swipeTracker.connect('update', this._onSwipeUpdate.bind(this));
|
|
|
|
swipeTracker.connect('end', this._onSwipeEnd.bind(this));
|
|
|
|
this._swipeTracker = swipeTracker;
|
2022-11-16 10:22:18 +00:00
|
|
|
|
|
|
|
this.connect('destroy', () => this._onDestroy());
|
2022-11-16 10:41:36 +00:00
|
|
|
|
|
|
|
this.bind_property(
|
|
|
|
'visible', this._swipeTracker, 'enabled',
|
|
|
|
GObject.BindingFlags.DEFAULT);
|
2022-11-16 10:22:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_onDestroy() {
|
|
|
|
if (this._swipeTracker) {
|
|
|
|
this._swipeTracker.destroy();
|
|
|
|
delete this._swipeTracker;
|
|
|
|
}
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get delta() {
|
|
|
|
return this._delta;
|
|
|
|
}
|
|
|
|
|
|
|
|
set delta(value) {
|
2019-07-25 22:13:19 +00:00
|
|
|
if (this._delta == value)
|
|
|
|
return;
|
|
|
|
|
2017-12-22 15:02:51 +00:00
|
|
|
this._delta = value;
|
2019-07-25 22:13:19 +00:00
|
|
|
this.notify('delta');
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
let followingPage = this.getFollowingPage();
|
|
|
|
|
|
|
|
if (this._followingPage != followingPage) {
|
|
|
|
if (this._followingPanel) {
|
|
|
|
this._followingPanel.destroy();
|
|
|
|
this._followingPanel = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (followingPage != null) {
|
|
|
|
this._followingPanel = this._generatePanel(followingPage);
|
2019-07-25 16:53:00 +00:00
|
|
|
this.add_child(this._followingPanel);
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this._followingPage = followingPage;
|
|
|
|
}
|
|
|
|
|
2022-06-29 18:26:27 +00:00
|
|
|
const multiplier = this.text_direction === Clutter.TextDirection.RTL
|
|
|
|
? -1 : 1;
|
2017-12-22 15:02:51 +00:00
|
|
|
|
2022-06-29 18:26:27 +00:00
|
|
|
this._panel.translation_x = value * multiplier;
|
2017-12-22 15:02:51 +00:00
|
|
|
if (this._followingPanel) {
|
2022-06-29 18:26:27 +00:00
|
|
|
const translation = value < 0
|
|
|
|
? this._width + EMOJI_PAGE_SEPARATION
|
|
|
|
: -this._width - EMOJI_PAGE_SEPARATION;
|
|
|
|
|
|
|
|
this._followingPanel.translation_x =
|
|
|
|
(value * multiplier) + (translation * multiplier);
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_prevPage(nPage) {
|
|
|
|
return (nPage + this._pages.length - 1) % this._pages.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
_nextPage(nPage) {
|
|
|
|
return (nPage + 1) % this._pages.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
getFollowingPage() {
|
|
|
|
if (this.delta == 0)
|
|
|
|
return null;
|
|
|
|
|
2022-06-29 18:26:27 +00:00
|
|
|
if (this.delta < 0)
|
2017-12-22 15:02:51 +00:00
|
|
|
return this._nextPage(this._curPage);
|
|
|
|
else
|
|
|
|
return this._prevPage(this._curPage);
|
|
|
|
}
|
|
|
|
|
2022-06-28 21:47:04 +00:00
|
|
|
_onSwipeUpdate(tracker, progress) {
|
|
|
|
this.delta = -progress * this._width;
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
if (this._currentKey != null) {
|
|
|
|
this._currentKey.cancel();
|
|
|
|
this._currentKey = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-06-28 21:47:04 +00:00
|
|
|
_onSwipeBegin(tracker) {
|
2019-07-25 16:53:00 +00:00
|
|
|
this._width = this.width;
|
2022-06-28 21:47:04 +00:00
|
|
|
const points = [-1, 0, 1];
|
|
|
|
tracker.confirmSwipe(this._width, points, 0, 0);
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
|
2022-06-28 21:47:04 +00:00
|
|
|
_onSwipeEnd(tracker, duration, endProgress) {
|
|
|
|
this.remove_all_transitions();
|
|
|
|
if (endProgress === 0) {
|
|
|
|
this.ease_property('delta', 0, {duration});
|
2017-12-22 15:02:51 +00:00
|
|
|
} else {
|
2022-06-29 18:26:27 +00:00
|
|
|
const value = endProgress < 0
|
|
|
|
? this._width + EMOJI_PAGE_SEPARATION
|
|
|
|
: -this._width - EMOJI_PAGE_SEPARATION;
|
2019-07-25 00:06:05 +00:00
|
|
|
this.ease_property('delta', value, {
|
2022-06-28 21:47:04 +00:00
|
|
|
duration,
|
2019-07-25 00:06:05 +00:00
|
|
|
onComplete: () => {
|
|
|
|
this.setCurrentPage(this.getFollowingPage());
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-25 00:06:05 +00:00
|
|
|
});
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_initPagingInfo() {
|
2022-06-28 16:53:56 +00:00
|
|
|
this._pages = [];
|
|
|
|
|
2017-12-22 15:02:51 +00:00
|
|
|
for (let i = 0; i < this._sections.length; i++) {
|
|
|
|
let section = this._sections[i];
|
|
|
|
let itemsPerPage = this._nCols * this._nRows;
|
|
|
|
let nPages = Math.ceil(section.keys.length / itemsPerPage);
|
|
|
|
let page = -1;
|
|
|
|
let pageKeys;
|
|
|
|
|
|
|
|
for (let j = 0; j < section.keys.length; j++) {
|
|
|
|
if (j % itemsPerPage == 0) {
|
|
|
|
page++;
|
|
|
|
pageKeys = [];
|
|
|
|
this._pages.push({ pageKeys, nPages, page, section: this._sections[i] });
|
|
|
|
}
|
|
|
|
|
|
|
|
pageKeys.push(section.keys[j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_lookupSection(section, nPage) {
|
|
|
|
for (let i = 0; i < this._pages.length; i++) {
|
|
|
|
let page = this._pages[i];
|
|
|
|
|
|
|
|
if (page.section == section && page.page == nPage)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
_generatePanel(nPage) {
|
2020-03-29 21:51:13 +00:00
|
|
|
const gridLayout = new Clutter.GridLayout({
|
|
|
|
orientation: Clutter.Orientation.HORIZONTAL,
|
|
|
|
column_homogeneous: true,
|
|
|
|
row_homogeneous: true,
|
|
|
|
});
|
|
|
|
const panel = new St.Widget({
|
|
|
|
layout_manager: gridLayout,
|
|
|
|
style_class: 'emoji-page',
|
|
|
|
x_expand: true,
|
|
|
|
y_expand: true,
|
|
|
|
});
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
/* Set an expander actor so all proportions are right despite the panel
|
|
|
|
* not having all rows/cols filled in.
|
|
|
|
*/
|
|
|
|
let expander = new Clutter.Actor();
|
|
|
|
gridLayout.attach(expander, 0, 0, this._nCols, this._nRows);
|
|
|
|
|
|
|
|
let page = this._pages[nPage];
|
|
|
|
let col = 0;
|
|
|
|
let row = 0;
|
|
|
|
|
|
|
|
for (let i = 0; i < page.pageKeys.length; i++) {
|
|
|
|
let modelKey = page.pageKeys[i];
|
2022-04-14 12:46:03 +00:00
|
|
|
let key = new Key({commitString: modelKey.label}, modelKey.variants);
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
key.keyButton.set_button_mask(0);
|
|
|
|
|
2022-04-14 18:49:00 +00:00
|
|
|
key.connect('pressed', () => {
|
2017-12-22 15:02:51 +00:00
|
|
|
this._currentKey = key;
|
|
|
|
});
|
2022-04-14 18:42:30 +00:00
|
|
|
key.connect('commit', (actor, keyval, str) => {
|
2017-12-22 15:02:51 +00:00
|
|
|
if (this._currentKey != key)
|
|
|
|
return;
|
|
|
|
this._currentKey = null;
|
|
|
|
this.emit('emoji', str);
|
|
|
|
});
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
gridLayout.attach(key, col, row, 1, 1);
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
col++;
|
|
|
|
if (col >= this._nCols) {
|
|
|
|
col = 0;
|
|
|
|
row++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return panel;
|
|
|
|
}
|
|
|
|
|
|
|
|
setCurrentPage(nPage) {
|
|
|
|
if (this._curPage == nPage)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._curPage = nPage;
|
|
|
|
|
|
|
|
if (this._panel) {
|
|
|
|
this._panel.destroy();
|
|
|
|
this._panel = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reuse followingPage if possible */
|
|
|
|
if (nPage == this._followingPage) {
|
|
|
|
this._panel = this._followingPanel;
|
|
|
|
this._followingPanel = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._followingPanel)
|
|
|
|
this._followingPanel.destroy();
|
|
|
|
|
|
|
|
this._followingPanel = null;
|
|
|
|
this._followingPage = null;
|
|
|
|
this._delta = 0;
|
|
|
|
|
|
|
|
if (!this._panel) {
|
|
|
|
this._panel = this._generatePanel(nPage);
|
2019-07-25 16:53:00 +00:00
|
|
|
this.add_child(this._panel);
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let page = this._pages[nPage];
|
2019-07-16 09:24:13 +00:00
|
|
|
this.emit('page-changed', page.section.label, page.page, page.nPages);
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
setCurrentSection(section, nPage) {
|
|
|
|
for (let i = 0; i < this._pages.length; i++) {
|
|
|
|
let page = this._pages[i];
|
|
|
|
|
|
|
|
if (page.section == section && page.page == nPage) {
|
|
|
|
this.setCurrentPage(i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-28 16:53:56 +00:00
|
|
|
|
|
|
|
setRatio(nCols, nRows) {
|
|
|
|
this._nCols = nCols;
|
|
|
|
this._nRows = nRows;
|
|
|
|
this._initPagingInfo();
|
|
|
|
}
|
2019-07-25 16:53:00 +00:00
|
|
|
});
|
2017-12-22 15:02:51 +00:00
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
var EmojiSelection = GObject.registerClass({
|
|
|
|
Signals: {
|
|
|
|
'emoji-selected': { param_types: [GObject.TYPE_STRING] },
|
|
|
|
'close-request': {},
|
|
|
|
'toggle': {},
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2022-06-28 16:53:56 +00:00
|
|
|
}, class EmojiSelection extends St.Widget {
|
2019-07-16 09:24:13 +00:00
|
|
|
_init() {
|
2022-06-28 16:53:56 +00:00
|
|
|
const gridLayout = new Clutter.GridLayout({
|
|
|
|
orientation: Clutter.Orientation.HORIZONTAL,
|
|
|
|
column_homogeneous: true,
|
|
|
|
row_homogeneous: true,
|
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
super._init({
|
2022-06-28 16:53:56 +00:00
|
|
|
layout_manager: gridLayout,
|
2019-07-16 09:24:13 +00:00
|
|
|
style_class: 'emoji-panel',
|
|
|
|
x_expand: true,
|
|
|
|
y_expand: true,
|
2022-06-29 18:26:27 +00:00
|
|
|
text_direction: global.stage.text_direction,
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
|
|
|
|
2017-12-22 15:02:51 +00:00
|
|
|
this._sections = [
|
|
|
|
{ first: 'grinning face', label: '🙂️' },
|
|
|
|
{ first: 'selfie', label: '👍️' },
|
|
|
|
{ first: 'monkey face', label: '🌷️' },
|
|
|
|
{ first: 'grapes', label: '🍴️' },
|
|
|
|
{ first: 'globe showing Europe-Africa', label: '✈️' },
|
|
|
|
{ first: 'jack-o-lantern', label: '🏃️' },
|
|
|
|
{ first: 'muted speaker', label: '🔔️' },
|
|
|
|
{ first: 'ATM sign', label: '❤️' },
|
|
|
|
{ first: 'chequered flag', label: '🚩️' },
|
|
|
|
];
|
|
|
|
|
2022-06-28 16:53:56 +00:00
|
|
|
this._gridLayout = gridLayout;
|
2017-12-22 15:02:51 +00:00
|
|
|
this._populateSections();
|
|
|
|
|
2022-06-28 16:53:56 +00:00
|
|
|
this._pagerBox = new Clutter.Actor({
|
|
|
|
layout_manager: new Clutter.BoxLayout({
|
|
|
|
orientation: Clutter.Orientation.VERTICAL,
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
|
|
|
this._emojiPager = new EmojiPager(this._sections);
|
2019-07-16 09:24:13 +00:00
|
|
|
this._emojiPager.connect('page-changed', (pager, sectionLabel, page, nPages) => {
|
|
|
|
this._onPageChanged(sectionLabel, page, nPages);
|
2017-12-22 15:02:51 +00:00
|
|
|
});
|
|
|
|
this._emojiPager.connect('emoji', (pager, str) => {
|
|
|
|
this.emit('emoji-selected', str);
|
|
|
|
});
|
2022-06-28 16:53:56 +00:00
|
|
|
this._pagerBox.add_child(this._emojiPager);
|
2017-12-22 15:02:51 +00:00
|
|
|
|
2019-09-10 05:36:58 +00:00
|
|
|
this._pageIndicator = new PageIndicators.PageIndicators(
|
2020-04-03 23:52:29 +00:00
|
|
|
Clutter.Orientation.HORIZONTAL);
|
2022-06-28 16:53:56 +00:00
|
|
|
this._pageIndicator.y_expand = false;
|
|
|
|
this._pageIndicator.y_align = Clutter.ActorAlign.START;
|
|
|
|
this._pagerBox.add_child(this._pageIndicator);
|
2017-12-22 15:02:51 +00:00
|
|
|
this._pageIndicator.setReactive(false);
|
|
|
|
|
2019-11-21 22:24:46 +00:00
|
|
|
this._emojiPager.connect('notify::delta', () => {
|
|
|
|
this._updateIndicatorPosition();
|
|
|
|
});
|
|
|
|
|
2022-06-28 16:53:56 +00:00
|
|
|
this._bottomRow = this._createBottomRow();
|
2017-12-22 15:02:51 +00:00
|
|
|
|
2019-11-21 22:24:46 +00:00
|
|
|
this._curPage = 0;
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
|
2019-09-10 05:42:48 +00:00
|
|
|
vfunc_map() {
|
|
|
|
this._emojiPager.setCurrentPage(0);
|
|
|
|
super.vfunc_map();
|
|
|
|
}
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
_onPageChanged(sectionLabel, page, nPages) {
|
2019-11-21 22:24:46 +00:00
|
|
|
this._curPage = page;
|
2017-12-22 15:02:51 +00:00
|
|
|
this._pageIndicator.setNPages(nPages);
|
2019-11-21 22:24:46 +00:00
|
|
|
this._updateIndicatorPosition();
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
for (let i = 0; i < this._sections.length; i++) {
|
|
|
|
let sect = this._sections[i];
|
2019-07-16 09:24:13 +00:00
|
|
|
sect.button.setLatched(sectionLabel == sect.label);
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 22:24:46 +00:00
|
|
|
_updateIndicatorPosition() {
|
|
|
|
this._pageIndicator.setCurrentPosition(this._curPage -
|
|
|
|
this._emojiPager.delta / this._emojiPager.width);
|
|
|
|
}
|
|
|
|
|
2017-12-22 15:02:51 +00:00
|
|
|
_findSection(emoji) {
|
|
|
|
for (let i = 0; i < this._sections.length; i++) {
|
|
|
|
if (this._sections[i].first == emoji)
|
|
|
|
return this._sections[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
_populateSections() {
|
|
|
|
let file = Gio.File.new_for_uri('resource:///org/gnome/shell/osk-layouts/emoji.json');
|
2019-01-31 14:08:00 +00:00
|
|
|
let [success_, contents] = file.load_contents(null);
|
2017-12-22 15:02:51 +00:00
|
|
|
|
2021-08-12 14:38:57 +00:00
|
|
|
let emoji = JSON.parse(new TextDecoder().decode(contents));
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
let variants = [];
|
|
|
|
let currentKey = 0;
|
|
|
|
let currentSection = null;
|
|
|
|
|
|
|
|
for (let i = 0; i < emoji.length; i++) {
|
|
|
|
/* Group variants of a same emoji so they appear on the key popover */
|
|
|
|
if (emoji[i].name.startsWith(emoji[currentKey].name)) {
|
|
|
|
variants.push(emoji[i].char);
|
|
|
|
if (i < emoji.length - 1)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
let newSection = this._findSection(emoji[currentKey].name);
|
|
|
|
if (newSection != null) {
|
|
|
|
currentSection = newSection;
|
|
|
|
currentSection.keys = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create the key */
|
|
|
|
let label = emoji[currentKey].char + String.fromCharCode(0xFE0F);
|
|
|
|
currentSection.keys.push({ label, variants });
|
|
|
|
currentKey = i;
|
|
|
|
variants = [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_createBottomRow() {
|
|
|
|
let row = new KeyContainer();
|
|
|
|
let key;
|
|
|
|
|
|
|
|
row.appendRow();
|
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
key = new Key({label: 'ABC'}, []);
|
2017-12-22 15:02:51 +00:00
|
|
|
key.keyButton.add_style_class_name('default-key');
|
2019-01-28 00:42:00 +00:00
|
|
|
key.connect('released', () => this.emit('toggle'));
|
2019-07-16 09:24:13 +00:00
|
|
|
row.appendKey(key, 1.5);
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
for (let i = 0; i < this._sections.length; i++) {
|
|
|
|
let section = this._sections[i];
|
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
key = new Key({label: section.label}, []);
|
2019-01-28 00:42:00 +00:00
|
|
|
key.connect('released', () => this._emojiPager.setCurrentSection(section, 0));
|
2019-07-16 09:24:13 +00:00
|
|
|
row.appendKey(key);
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
section.button = key;
|
|
|
|
}
|
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
key = new Key({iconName: 'go-down-symbolic'});
|
2017-12-22 15:02:51 +00:00
|
|
|
key.keyButton.add_style_class_name('default-key');
|
|
|
|
key.keyButton.add_style_class_name('hide-key');
|
|
|
|
key.connect('released', () => {
|
2019-08-28 20:06:14 +00:00
|
|
|
this.emit('close-request');
|
2017-12-22 15:02:51 +00:00
|
|
|
});
|
2019-07-16 09:24:13 +00:00
|
|
|
row.appendKey(key);
|
2017-12-22 15:02:51 +00:00
|
|
|
row.layoutButtons();
|
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
const actor = new AspectContainer({
|
|
|
|
layout_manager: new Clutter.BinLayout(),
|
|
|
|
x_expand: true,
|
|
|
|
y_expand: true,
|
|
|
|
});
|
2019-03-05 18:34:23 +00:00
|
|
|
actor.add_child(row);
|
|
|
|
|
|
|
|
return actor;
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
2022-06-28 16:53:56 +00:00
|
|
|
|
|
|
|
setRatio(nCols, nRows) {
|
|
|
|
this._emojiPager.setRatio(Math.floor(nCols), Math.floor(nRows) - 1);
|
|
|
|
this._bottomRow.setRatio(nCols, 1);
|
|
|
|
|
|
|
|
// (Re)attach actors so the emoji panel fits the ratio and
|
|
|
|
// the bottom row is ensured to take 1 row high.
|
|
|
|
if (this._pagerBox.get_parent())
|
|
|
|
this.remove_child(this._pagerBox);
|
|
|
|
if (this._bottomRow.get_parent())
|
|
|
|
this.remove_child(this._bottomRow);
|
|
|
|
|
|
|
|
this._gridLayout.attach(this._pagerBox, 0, 0, 1, Math.floor(nRows) - 1);
|
|
|
|
this._gridLayout.attach(this._bottomRow, 0, Math.floor(nRows) - 1, 1, 1);
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2017-12-22 15:02:51 +00:00
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
var Keypad = GObject.registerClass({
|
|
|
|
Signals: {
|
|
|
|
'keyval': { param_types: [GObject.TYPE_UINT] },
|
2019-08-20 21:43:54 +00:00
|
|
|
},
|
2019-07-16 09:24:13 +00:00
|
|
|
}, class Keypad extends AspectContainer {
|
|
|
|
_init() {
|
2019-01-19 14:58:45 +00:00
|
|
|
let keys = [
|
|
|
|
{ label: '1', keyval: Clutter.KEY_1, left: 0, top: 0 },
|
|
|
|
{ label: '2', keyval: Clutter.KEY_2, left: 1, top: 0 },
|
|
|
|
{ label: '3', keyval: Clutter.KEY_3, left: 2, top: 0 },
|
|
|
|
{ label: '4', keyval: Clutter.KEY_4, left: 0, top: 1 },
|
|
|
|
{ label: '5', keyval: Clutter.KEY_5, left: 1, top: 1 },
|
|
|
|
{ label: '6', keyval: Clutter.KEY_6, left: 2, top: 1 },
|
|
|
|
{ label: '7', keyval: Clutter.KEY_7, left: 0, top: 2 },
|
|
|
|
{ label: '8', keyval: Clutter.KEY_8, left: 1, top: 2 },
|
|
|
|
{ label: '9', keyval: Clutter.KEY_9, left: 2, top: 2 },
|
|
|
|
{ label: '0', keyval: Clutter.KEY_0, left: 1, top: 3 },
|
2020-08-06 10:28:03 +00:00
|
|
|
{ keyval: Clutter.KEY_BackSpace, icon: 'edit-clear-symbolic', left: 3, top: 0 },
|
|
|
|
{ keyval: Clutter.KEY_Return, extraClassName: 'enter-key', icon: 'keyboard-enter-symbolic', left: 3, top: 1, height: 2 },
|
2019-01-19 14:58:45 +00:00
|
|
|
];
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
super._init({
|
|
|
|
layout_manager: new Clutter.BinLayout(),
|
|
|
|
x_expand: true,
|
2019-08-20 21:43:54 +00:00
|
|
|
y_expand: true,
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2019-01-19 14:58:45 +00:00
|
|
|
|
2020-03-29 21:51:13 +00:00
|
|
|
const gridLayout = new Clutter.GridLayout({
|
|
|
|
orientation: Clutter.Orientation.HORIZONTAL,
|
|
|
|
column_homogeneous: true,
|
|
|
|
row_homogeneous: true,
|
|
|
|
});
|
2019-01-19 14:58:45 +00:00
|
|
|
this._box = new St.Widget({ layout_manager: gridLayout, x_expand: true, y_expand: true });
|
2019-07-16 09:24:13 +00:00
|
|
|
this.add_child(this._box);
|
2019-01-19 14:58:45 +00:00
|
|
|
|
|
|
|
for (let i = 0; i < keys.length; i++) {
|
|
|
|
let cur = keys[i];
|
2022-04-14 12:46:03 +00:00
|
|
|
let key = new Key({
|
|
|
|
label: cur.label,
|
|
|
|
iconName: cur.icon,
|
|
|
|
});
|
2019-01-19 14:58:45 +00:00
|
|
|
|
|
|
|
if (keys[i].extraClassName)
|
|
|
|
key.keyButton.add_style_class_name(cur.extraClassName);
|
|
|
|
|
|
|
|
let w, h;
|
|
|
|
w = cur.width || 1;
|
|
|
|
h = cur.height || 1;
|
2019-07-16 09:24:13 +00:00
|
|
|
gridLayout.attach(key, cur.left, cur.top, w, h);
|
2019-01-19 14:58:45 +00:00
|
|
|
|
|
|
|
key.connect('released', () => {
|
|
|
|
this.emit('keyval', cur.keyval);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2019-01-19 14:58:45 +00:00
|
|
|
|
2022-08-09 14:00:33 +00:00
|
|
|
var KeyboardManager = class extends Signals.EventEmitter {
|
2019-08-28 21:52:52 +00:00
|
|
|
constructor() {
|
2022-08-09 14:00:33 +00:00
|
|
|
super();
|
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
this._keyboard = null;
|
|
|
|
this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA });
|
|
|
|
this._a11yApplicationsSettings.connect('changed', this._syncEnabled.bind(this));
|
|
|
|
|
2020-02-10 19:03:30 +00:00
|
|
|
this._seat = Clutter.get_default_backend().get_default_seat();
|
|
|
|
this._seat.connect('notify::touch-mode', this._syncEnabled.bind(this));
|
|
|
|
|
2019-10-05 10:34:07 +00:00
|
|
|
this._lastDevice = null;
|
2020-04-21 16:04:56 +00:00
|
|
|
global.backend.connect('last-device-changed', (backend, device) => {
|
2020-03-28 13:15:09 +00:00
|
|
|
if (device.device_type === Clutter.InputDeviceType.KEYBOARD_DEVICE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._lastDevice = device;
|
|
|
|
this._syncEnabled();
|
2019-08-28 21:52:52 +00:00
|
|
|
});
|
2021-02-11 12:41:16 +00:00
|
|
|
|
|
|
|
const mode = Shell.ActionMode.ALL & ~Shell.ActionMode.LOCK_SCREEN;
|
|
|
|
const bottomDragAction = new EdgeDragAction.EdgeDragAction(St.Side.BOTTOM, mode);
|
|
|
|
bottomDragAction.connect('activated', () => {
|
2021-02-14 09:26:38 +00:00
|
|
|
if (this._keyboard)
|
|
|
|
this._keyboard.gestureActivate(Main.layoutManager.bottomIndex);
|
2021-02-11 23:54:55 +00:00
|
|
|
});
|
|
|
|
bottomDragAction.connect('progress', (_action, progress) => {
|
2021-02-14 09:26:38 +00:00
|
|
|
if (this._keyboard)
|
|
|
|
this._keyboard.gestureProgress(progress);
|
|
|
|
});
|
|
|
|
bottomDragAction.connect('gesture-cancel', () => {
|
|
|
|
if (this._keyboard)
|
|
|
|
this._keyboard.gestureCancel();
|
2021-02-11 12:41:16 +00:00
|
|
|
});
|
2022-05-24 20:22:05 +00:00
|
|
|
global.stage.add_action_full('osk', Clutter.EventPhase.CAPTURE, bottomDragAction);
|
2021-02-11 13:10:48 +00:00
|
|
|
this._bottomDragAction = bottomDragAction;
|
2021-02-11 12:41:16 +00:00
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
this._syncEnabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
_lastDeviceIsTouchscreen() {
|
2019-10-05 10:34:07 +00:00
|
|
|
if (!this._lastDevice)
|
2019-08-28 21:52:52 +00:00
|
|
|
return false;
|
|
|
|
|
2019-10-05 10:34:07 +00:00
|
|
|
let deviceType = this._lastDevice.get_device_type();
|
|
|
|
return deviceType == Clutter.InputDeviceType.TOUCHSCREEN_DEVICE;
|
2019-08-28 21:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_syncEnabled() {
|
|
|
|
let enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD);
|
2020-02-10 19:03:30 +00:00
|
|
|
let autoEnabled = this._seat.get_touch_mode() && this._lastDeviceIsTouchscreen();
|
|
|
|
let enabled = enableKeyboard || autoEnabled;
|
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
if (!enabled && !this._keyboard)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (enabled && !this._keyboard) {
|
|
|
|
this._keyboard = new Keyboard();
|
2021-02-11 13:10:48 +00:00
|
|
|
this._keyboard.connect('visibility-changed', () => {
|
2022-08-09 14:00:33 +00:00
|
|
|
this.emit('visibility-changed');
|
2021-02-11 13:10:48 +00:00
|
|
|
this._bottomDragAction.enabled = !this._keyboard.visible;
|
|
|
|
});
|
2019-08-28 21:52:52 +00:00
|
|
|
} else if (!enabled && this._keyboard) {
|
|
|
|
this._keyboard.setCursorLocation(null);
|
|
|
|
this._keyboard.destroy();
|
|
|
|
this._keyboard = null;
|
2021-02-11 13:10:48 +00:00
|
|
|
this._bottomDragAction.enabled = true;
|
2019-08-28 21:52:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
get keyboardActor() {
|
2019-07-16 09:24:13 +00:00
|
|
|
return this._keyboard;
|
2019-08-28 21:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get visible() {
|
2019-07-16 09:24:13 +00:00
|
|
|
return this._keyboard && this._keyboard.visible;
|
2019-08-28 21:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
open(monitor) {
|
2021-02-11 15:44:28 +00:00
|
|
|
Main.layoutManager.keyboardIndex = monitor;
|
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
if (this._keyboard)
|
2021-02-11 15:44:28 +00:00
|
|
|
this._keyboard.open();
|
2019-08-28 21:52:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
close() {
|
|
|
|
if (this._keyboard)
|
|
|
|
this._keyboard.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
addSuggestion(text, callback) {
|
|
|
|
if (this._keyboard)
|
|
|
|
this._keyboard.addSuggestion(text, callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
resetSuggestions() {
|
|
|
|
if (this._keyboard)
|
|
|
|
this._keyboard.resetSuggestions();
|
|
|
|
}
|
|
|
|
|
2022-07-03 14:44:39 +00:00
|
|
|
setSuggestionsVisible(visible) {
|
|
|
|
this._keyboard?.setSuggestionsVisible(visible);
|
|
|
|
}
|
|
|
|
|
2022-03-09 22:11:03 +00:00
|
|
|
maybeHandleEvent(event) {
|
2019-08-28 21:52:52 +00:00
|
|
|
if (!this._keyboard)
|
|
|
|
return false;
|
|
|
|
|
2022-02-25 12:09:17 +00:00
|
|
|
const actor = global.stage.get_event_actor(event);
|
2022-03-09 22:11:03 +00:00
|
|
|
|
|
|
|
if (Main.layoutManager.keyboardBox.contains(actor) ||
|
|
|
|
!!actor._extendedKeys || !!actor.extendedKey) {
|
2022-04-06 10:50:32 +00:00
|
|
|
actor.event(event, true);
|
|
|
|
actor.event(event, false);
|
2022-03-09 22:11:03 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2019-08-28 21:52:52 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-02-11 13:10:48 +00:00
|
|
|
var Keyboard = GObject.registerClass({
|
|
|
|
Signals: {
|
|
|
|
'visibility-changed': {},
|
|
|
|
},
|
|
|
|
}, class Keyboard extends St.BoxLayout {
|
2019-07-16 09:24:13 +00:00
|
|
|
_init() {
|
2022-06-30 09:41:12 +00:00
|
|
|
super._init({
|
|
|
|
name: 'keyboard',
|
|
|
|
reactive: true,
|
|
|
|
// Keyboard models are defined in LTR, we must override
|
|
|
|
// the locale setting in order to avoid flipping the
|
|
|
|
// keyboard on RTL locales.
|
|
|
|
text_direction: Clutter.TextDirection.LTR,
|
|
|
|
vertical: true,
|
|
|
|
});
|
2012-09-24 18:03:53 +00:00
|
|
|
this._focusInExtendedKeys = false;
|
2017-12-22 15:02:51 +00:00
|
|
|
this._emojiActive = false;
|
2011-09-01 15:01:48 +00:00
|
|
|
|
2018-01-23 15:45:46 +00:00
|
|
|
this._languagePopup = null;
|
2021-02-11 14:36:42 +00:00
|
|
|
this._focusWindow = null;
|
keyboard: Animate focus window using position instead of translation-y
Commit 8526776b4a53d9a801197fb66ce9a282323c1c4e changed the OSK to use
the translation-y property of the MetaWindowActor when animating a focus
window, which broke two things:
1) It's not compatible with the obscured region culling we do for
windows in mutter. That's because MetaCullable strictly operates in
integer coordinates and thus has to ignore any transformations
(translation-y is a transformation). Because of this, during the
animation and gesture, window damage is now tracked incorrectly,
introducing painting issues. The best fix for this would probably be
factoring in transformations when tracking damage in MetaCullable, but
that's not feasible right now.
2) It broke the shifting up of maximized and tiled windows, likely that
is because they are positioned using constraints internally, and mutter
enforces those constraints every time meta_window_move_frame() is
called, not allowing the window to move somewhere else.
To fix both issues, go back to the old way of shifting the window for
now, using the fixed y position of the ClutterActor. To make sure the
drag-up gesture still works, store the initial y1 position of the window
and then use that as a reference point for all our animations.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1760>
2021-02-20 11:55:58 +00:00
|
|
|
this._focusWindowStartY = null;
|
2017-01-16 15:21:01 +00:00
|
|
|
|
2018-02-15 11:20:51 +00:00
|
|
|
this._latched = false; // current level is latched
|
2022-04-16 11:06:09 +00:00
|
|
|
this._modifiers = new Set();
|
|
|
|
this._modifierKeys = new Map();
|
2017-10-09 10:51:08 +00:00
|
|
|
|
2017-12-06 12:46:02 +00:00
|
|
|
this._suggestions = null;
|
2019-04-26 08:23:28 +00:00
|
|
|
this._emojiKeyVisible = Meta.is_wayland_compositor();
|
2015-03-10 16:49:56 +00:00
|
|
|
|
2018-05-25 09:35:49 +00:00
|
|
|
this._focusTracker = new FocusTracker();
|
2021-08-15 22:36:59 +00:00
|
|
|
this._focusTracker.connectObject(
|
|
|
|
'position-changed', this._onFocusPositionChanged.bind(this),
|
|
|
|
'window-grabbed', this._onFocusWindowMoving.bind(this), this);
|
2021-02-26 15:08:20 +00:00
|
|
|
|
|
|
|
this._windowMovedId = this._focusTracker.connect('window-moved',
|
|
|
|
this._onFocusWindowMoving.bind(this));
|
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
// Valid only for X11
|
|
|
|
if (!Meta.is_wayland_compositor()) {
|
2021-08-15 22:36:59 +00:00
|
|
|
this._focusTracker.connectObject('focus-changed', (_tracker, focused) => {
|
2019-08-28 21:52:52 +00:00
|
|
|
if (focused)
|
|
|
|
this.open(Main.layoutManager.focusIndex);
|
|
|
|
else
|
|
|
|
this.close();
|
2021-08-15 22:36:59 +00:00
|
|
|
}, this);
|
2019-08-28 21:52:52 +00:00
|
|
|
}
|
2012-10-30 15:46:18 +00:00
|
|
|
|
|
|
|
this._showIdleId = 0;
|
2012-11-18 15:17:21 +00:00
|
|
|
|
|
|
|
this._keyboardVisible = false;
|
|
|
|
this._keyboardRequested = false;
|
|
|
|
this._keyboardRestingId = 0;
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.layoutManager.connectObject('monitors-changed',
|
|
|
|
this._relayout.bind(this), this);
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
this._setupKeyboard();
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this.connect('destroy', this._onDestroy.bind(this));
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-01-16 15:21:01 +00:00
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
get visible() {
|
2019-07-16 09:24:13 +00:00
|
|
|
return this._keyboardVisible && super.visible;
|
2019-08-28 21:52:52 +00:00
|
|
|
}
|
2015-03-10 16:49:56 +00:00
|
|
|
|
2020-04-27 21:45:21 +00:00
|
|
|
set visible(visible) {
|
|
|
|
super.visible = visible;
|
|
|
|
}
|
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
_onFocusPositionChanged(focusTracker) {
|
|
|
|
let rect = focusTracker.getCurrentRect();
|
|
|
|
this.setCursorLocation(focusTracker.currentWindow, rect.x, rect.y, rect.width, rect.height);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2015-03-10 16:49:56 +00:00
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
_onDestroy() {
|
2021-02-26 15:08:20 +00:00
|
|
|
if (this._windowMovedId) {
|
|
|
|
this._focusTracker.disconnect(this._windowMovedId);
|
|
|
|
delete this._windowMovedId;
|
|
|
|
}
|
|
|
|
|
2021-02-26 13:34:36 +00:00
|
|
|
if (this._focusTracker) {
|
|
|
|
this._focusTracker.destroy();
|
|
|
|
delete this._focusTracker;
|
|
|
|
}
|
|
|
|
|
2019-08-28 21:52:52 +00:00
|
|
|
this._clearShowIdle();
|
2017-10-09 10:53:04 +00:00
|
|
|
|
2020-02-18 14:05:57 +00:00
|
|
|
this._keyboardController.destroy();
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
Main.layoutManager.untrackChrome(this);
|
|
|
|
Main.layoutManager.keyboardBox.remove_actor(this);
|
2021-02-11 12:08:47 +00:00
|
|
|
Main.layoutManager.keyboardBox.hide();
|
2018-01-23 15:45:46 +00:00
|
|
|
|
|
|
|
if (this._languagePopup) {
|
|
|
|
this._languagePopup.destroy();
|
|
|
|
this._languagePopup = null;
|
|
|
|
}
|
2022-11-16 12:26:44 +00:00
|
|
|
|
|
|
|
IBusManager.getIBusManager().setCompletionEnabled(false, () => Main.inputMethod.update());
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_setupKeyboard() {
|
2019-07-16 09:24:13 +00:00
|
|
|
Main.layoutManager.keyboardBox.add_actor(this);
|
|
|
|
Main.layoutManager.trackChrome(this);
|
2011-09-01 15:01:48 +00:00
|
|
|
|
2017-08-05 10:21:30 +00:00
|
|
|
this._keyboardController = new KeyboardController();
|
|
|
|
|
2011-08-29 15:11:22 +00:00
|
|
|
this._groups = {};
|
2019-05-09 19:37:20 +00:00
|
|
|
this._currentPage = null;
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-12-06 12:46:02 +00:00
|
|
|
this._suggestions = new Suggestions();
|
2019-10-21 18:44:00 +00:00
|
|
|
this.add_child(this._suggestions);
|
2017-12-06 12:46:02 +00:00
|
|
|
|
2020-01-21 14:00:18 +00:00
|
|
|
this._aspectContainer = new AspectContainer({
|
|
|
|
layout_manager: new Clutter.BinLayout(),
|
|
|
|
y_expand: true,
|
|
|
|
});
|
2019-10-21 18:44:00 +00:00
|
|
|
this.add_child(this._aspectContainer);
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
this._emojiSelection = new EmojiSelection();
|
|
|
|
this._emojiSelection.connect('toggle', this._toggleEmoji.bind(this));
|
2019-08-28 20:06:14 +00:00
|
|
|
this._emojiSelection.connect('close-request', () => this.close());
|
2017-12-22 15:02:51 +00:00
|
|
|
this._emojiSelection.connect('emoji-selected', (selection, emoji) => {
|
|
|
|
this._keyboardController.commitString(emoji);
|
|
|
|
});
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this._emojiSelection.hide();
|
2022-06-28 16:53:56 +00:00
|
|
|
this._aspectContainer.add_child(this._emojiSelection);
|
2019-01-21 20:39:27 +00:00
|
|
|
|
2019-01-19 14:58:45 +00:00
|
|
|
this._keypad = new Keypad();
|
2021-08-15 22:36:59 +00:00
|
|
|
this._keypad.connectObject('keyval', (_keypad, keyval) => {
|
2019-01-19 14:58:45 +00:00
|
|
|
this._keyboardController.keyvalPress(keyval);
|
|
|
|
this._keyboardController.keyvalRelease(keyval);
|
2021-08-15 22:36:59 +00:00
|
|
|
}, this);
|
2019-07-16 09:24:13 +00:00
|
|
|
this._aspectContainer.add_child(this._keypad);
|
|
|
|
this._keypad.hide();
|
2019-01-19 14:58:45 +00:00
|
|
|
this._keypadVisible = false;
|
|
|
|
|
2018-01-23 16:03:02 +00:00
|
|
|
this._ensureKeysForGroup(this._keyboardController.getCurrentGroup());
|
|
|
|
this._setActiveLayer(0);
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2022-11-16 10:42:46 +00:00
|
|
|
Main.inputMethod.connectObject(
|
|
|
|
'terminal-mode-changed', this._onTerminalModeChanged.bind(this),
|
|
|
|
this);
|
2022-04-22 19:16:52 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
this._keyboardController.connectObject(
|
|
|
|
'active-group', this._onGroupChanged.bind(this),
|
|
|
|
'groups-changed', this._onKeyboardGroupsChanged.bind(this),
|
|
|
|
'panel-state', this._onKeyboardStateChanged.bind(this),
|
|
|
|
'keypad-visible', this._onKeypadVisible.bind(this),
|
|
|
|
this);
|
|
|
|
global.stage.connectObject('notify::key-focus',
|
|
|
|
this._onKeyFocusChanged.bind(this), this);
|
2017-12-20 12:08:50 +00:00
|
|
|
|
2019-08-20 00:51:42 +00:00
|
|
|
if (Meta.is_wayland_compositor()) {
|
2021-08-15 22:36:59 +00:00
|
|
|
this._keyboardController.connectObject('emoji-visible',
|
|
|
|
this._onEmojiKeyVisible.bind(this), this);
|
2019-08-20 00:51:42 +00:00
|
|
|
}
|
2019-04-26 08:23:28 +00:00
|
|
|
|
2017-12-20 12:08:50 +00:00
|
|
|
this._relayout();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onKeyFocusChanged() {
|
2011-08-29 15:11:22 +00:00
|
|
|
let focus = global.stage.key_focus;
|
|
|
|
|
2011-10-11 21:54:05 +00:00
|
|
|
// Showing an extended key popup and clicking a key from the extended keys
|
|
|
|
// will grab focus, but ignore that
|
2012-09-24 18:03:53 +00:00
|
|
|
let extendedKeysWereFocused = this._focusInExtendedKeys;
|
2019-12-04 17:51:08 +00:00
|
|
|
this._focusInExtendedKeys = focus && (focus._extendedKeys || focus.extendedKey);
|
2012-09-24 18:03:53 +00:00
|
|
|
if (this._focusInExtendedKeys || extendedKeysWereFocused)
|
2011-08-29 15:11:22 +00:00
|
|
|
return;
|
|
|
|
|
2012-10-30 15:46:18 +00:00
|
|
|
if (!(focus instanceof Clutter.Text)) {
|
2019-08-28 20:06:14 +00:00
|
|
|
this.close();
|
2012-10-30 15:46:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-10 17:26:52 +00:00
|
|
|
if (!this._showIdleId) {
|
2019-01-29 19:36:54 +00:00
|
|
|
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => {
|
2019-08-28 20:06:14 +00:00
|
|
|
this.open(Main.layoutManager.focusIndex);
|
2019-01-29 19:36:54 +00:00
|
|
|
this._showIdleId = 0;
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
});
|
2019-08-28 21:52:52 +00:00
|
|
|
GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.open');
|
2014-04-10 17:26:52 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_createLayersForGroup(groupName) {
|
2017-08-05 10:21:30 +00:00
|
|
|
let keyboardModel = new KeyboardModel(groupName);
|
2013-02-18 03:01:04 +00:00
|
|
|
let layers = {};
|
2017-08-05 10:21:30 +00:00
|
|
|
let levels = keyboardModel.getLevels();
|
|
|
|
for (let i = 0; i < levels.length; i++) {
|
|
|
|
let currentLevel = levels[i];
|
|
|
|
/* There are keyboard maps which consist of 3 levels (no uppercase,
|
|
|
|
* basically). We however make things consistent by skipping that
|
|
|
|
* second level.
|
|
|
|
*/
|
2019-08-19 19:38:51 +00:00
|
|
|
let level = i >= 1 && levels.length == 3 ? i + 1 : i;
|
2017-12-20 12:08:50 +00:00
|
|
|
|
|
|
|
let layout = new KeyContainer();
|
2018-02-15 13:48:46 +00:00
|
|
|
layout.shiftKeys = [];
|
|
|
|
|
2017-08-05 10:21:30 +00:00
|
|
|
this._loadRows(currentLevel, level, levels.length, layout);
|
|
|
|
layers[level] = layout;
|
2019-01-21 20:39:27 +00:00
|
|
|
this._aspectContainer.add_child(layout);
|
2022-05-30 21:12:51 +00:00
|
|
|
layout.layoutButtons();
|
2013-02-18 03:01:04 +00:00
|
|
|
|
|
|
|
layout.hide();
|
|
|
|
}
|
2017-12-22 15:02:51 +00:00
|
|
|
|
2013-02-18 03:01:04 +00:00
|
|
|
return layers;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-02-18 03:01:04 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_ensureKeysForGroup(group) {
|
2018-01-23 16:03:02 +00:00
|
|
|
if (!this._groups[group])
|
|
|
|
this._groups[group] = this._createLayersForGroup(group);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_addRowKeys(keys, layout) {
|
2011-08-29 15:11:22 +00:00
|
|
|
for (let i = 0; i < keys.length; ++i) {
|
2022-04-14 12:46:03 +00:00
|
|
|
const key = keys[i];
|
2022-04-13 14:54:59 +00:00
|
|
|
const {strings} = key;
|
2022-04-14 12:46:03 +00:00
|
|
|
const commitString = strings?.shift();
|
|
|
|
|
|
|
|
let button = new Key({
|
|
|
|
commitString,
|
|
|
|
label: key.label,
|
|
|
|
iconName: key.iconName,
|
|
|
|
keyval: key.keyval,
|
|
|
|
}, strings);
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2022-04-14 11:20:49 +00:00
|
|
|
if (key.width !== null)
|
|
|
|
button.setWidth(key.width);
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2022-04-16 11:06:09 +00:00
|
|
|
if (key.action !== 'modifier') {
|
2022-10-11 16:23:19 +00:00
|
|
|
button.connect('commit', (_actor, keyval, str) => {
|
|
|
|
this._commitAction(keyval, str);
|
2022-04-16 11:06:09 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-14 19:09:27 +00:00
|
|
|
if (key.action !== null) {
|
|
|
|
button.connect('released', () => {
|
2022-04-15 17:47:09 +00:00
|
|
|
if (key.action === 'hide') {
|
2022-04-14 19:09:27 +00:00
|
|
|
this.close();
|
2022-04-15 17:47:09 +00:00
|
|
|
} else if (key.action === 'languageMenu') {
|
2022-04-14 19:09:27 +00:00
|
|
|
this._popupLanguageMenu(button);
|
2022-04-15 17:47:09 +00:00
|
|
|
} else if (key.action === 'emoji') {
|
2022-04-14 19:09:27 +00:00
|
|
|
this._toggleEmoji();
|
2022-04-16 11:06:09 +00:00
|
|
|
} else if (key.action === 'modifier') {
|
|
|
|
this._toggleModifier(key.keyval);
|
2022-04-20 21:18:27 +00:00
|
|
|
} else if (key.action === 'delete') {
|
|
|
|
this._toggleDelete(true);
|
|
|
|
this._toggleDelete(false);
|
2022-04-15 17:47:09 +00:00
|
|
|
} else if (!this._longPressed && key.action === 'levelSwitch') {
|
|
|
|
this._setActiveLayer(key.level);
|
|
|
|
this._setLatched(
|
|
|
|
key.level === 1 &&
|
|
|
|
key.iconName === 'keyboard-caps-lock-symbolic');
|
|
|
|
}
|
|
|
|
|
|
|
|
this._longPressed = false;
|
2022-04-14 19:09:27 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key.action === 'levelSwitch' &&
|
|
|
|
key.iconName === 'keyboard-shift-symbolic') {
|
|
|
|
layout.shiftKeys.push(button);
|
|
|
|
if (key.level === 1) {
|
2022-04-15 17:47:09 +00:00
|
|
|
button.connect('long-press', () => {
|
|
|
|
this._setActiveLayer(key.level);
|
|
|
|
this._setLatched(true);
|
|
|
|
this._longPressed = true;
|
|
|
|
});
|
2022-04-14 19:09:27 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2022-04-20 21:18:27 +00:00
|
|
|
if (key.action === 'delete') {
|
|
|
|
button.connect('long-press',
|
|
|
|
() => this._toggleDelete(true));
|
|
|
|
}
|
|
|
|
|
2022-04-16 11:06:09 +00:00
|
|
|
if (key.action === 'modifier') {
|
|
|
|
let modifierKeys = this._modifierKeys[key.keyval] || [];
|
|
|
|
modifierKeys.push(button);
|
|
|
|
this._modifierKeys[key.keyval] = modifierKeys;
|
|
|
|
}
|
|
|
|
|
2022-04-14 12:46:03 +00:00
|
|
|
if (key.action || key.keyval)
|
|
|
|
button.keyButton.add_style_class_name('default-key');
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
layout.appendKey(button, button.keyButton.keyWidth);
|
2017-08-05 10:21:30 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-11-10 21:47:57 +00:00
|
|
|
|
2022-10-11 16:24:15 +00:00
|
|
|
async _commitAction(keyval, str) {
|
2022-10-11 16:23:19 +00:00
|
|
|
if (this._modifiers.size === 0 && str !== '' &&
|
|
|
|
keyval && this._oskCompletionEnabled) {
|
2022-10-11 16:24:15 +00:00
|
|
|
if (await Main.inputMethod.handleVirtualKey(keyval))
|
|
|
|
return;
|
2022-10-11 16:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this._latched)
|
|
|
|
this._setActiveLayer(0);
|
|
|
|
}
|
|
|
|
|
2022-04-20 21:18:27 +00:00
|
|
|
_previousWordPosition(text, cursor) {
|
|
|
|
/* Skip word prior to cursor */
|
|
|
|
let pos = Math.max(0, text.slice(0, cursor).search(/\s+\S+\s*$/));
|
|
|
|
if (pos < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Skip contiguous spaces */
|
|
|
|
for (; pos >= 0; pos--) {
|
|
|
|
if (text.charAt(pos) !== ' ')
|
|
|
|
return GLib.utf8_strlen(text.slice(0, pos + 1), -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
_toggleDelete(enabled) {
|
|
|
|
if (this._deleteEnabled === enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._deleteEnabled = enabled;
|
|
|
|
this._timesDeleted = 0;
|
|
|
|
|
2022-04-22 19:16:52 +00:00
|
|
|
if (!Main.inputMethod.currentFocus ||
|
|
|
|
Main.inputMethod.hasPreedit() ||
|
|
|
|
Main.inputMethod.terminalMode) {
|
2022-04-20 21:18:27 +00:00
|
|
|
/* If there is no IM focus or are in the middle of preedit,
|
|
|
|
* fallback to keypresses */
|
|
|
|
if (enabled)
|
|
|
|
this._keyboardController.keyvalPress(Clutter.KEY_BackSpace);
|
|
|
|
else
|
|
|
|
this._keyboardController.keyvalRelease(Clutter.KEY_BackSpace);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enabled) {
|
|
|
|
let func = (text, cursor) => {
|
|
|
|
if (cursor === 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let encoder = new TextEncoder();
|
|
|
|
let decoder = new TextDecoder();
|
|
|
|
|
|
|
|
/* Find cursor/anchor position in characters */
|
|
|
|
const cursorIdx = GLib.utf8_strlen(decoder.decode(encoder.encode(
|
|
|
|
text).slice(0, cursor)), -1);
|
|
|
|
const anchorIdx = this._timesDeleted < BACKSPACE_WORD_DELETE_THRESHOLD
|
|
|
|
? cursorIdx - 1
|
|
|
|
: this._previousWordPosition(text, cursor);
|
|
|
|
/* Now get offset from cursor */
|
|
|
|
const offset = anchorIdx - cursorIdx;
|
|
|
|
|
|
|
|
this._timesDeleted++;
|
|
|
|
Main.inputMethod.delete_surrounding(offset, Math.abs(offset));
|
|
|
|
};
|
|
|
|
|
|
|
|
this._surroundingUpdateId = Main.inputMethod.connect(
|
|
|
|
'surrounding-text-set', () => {
|
|
|
|
let [text, cursor] = Main.inputMethod.getSurroundingText();
|
|
|
|
if (this._timesDeleted === 0) {
|
|
|
|
func(text, cursor);
|
|
|
|
} else {
|
|
|
|
if (this._surroundingUpdateTimeoutId > 0) {
|
|
|
|
GLib.source_remove(this._surroundingUpdateTimeoutId);
|
|
|
|
this._surroundingUpdateTimeoutId = 0;
|
|
|
|
}
|
|
|
|
this._surroundingUpdateTimeoutId =
|
|
|
|
GLib.timeout_add(GLib.PRIORITY_DEFAULT, KEY_RELEASE_TIMEOUT, () => {
|
|
|
|
func(text, cursor);
|
|
|
|
this._surroundingUpdateTimeoutId = 0;
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let [text, cursor] = Main.inputMethod.getSurroundingText();
|
|
|
|
if (text)
|
|
|
|
func(text, cursor);
|
|
|
|
else
|
|
|
|
Main.inputMethod.request_surrounding();
|
|
|
|
} else {
|
|
|
|
if (this._surroundingUpdateId > 0) {
|
|
|
|
Main.inputMethod.disconnect(this._surroundingUpdateId);
|
|
|
|
this._surroundingUpdateId = 0;
|
|
|
|
}
|
|
|
|
if (this._surroundingUpdateTimeoutId > 0) {
|
|
|
|
GLib.source_remove(this._surroundingUpdateTimeoutId);
|
|
|
|
this._surroundingUpdateTimeoutId = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-14 19:09:27 +00:00
|
|
|
_setLatched(latched) {
|
|
|
|
this._latched = latched;
|
|
|
|
this._setCurrentLevelLatched(this._currentPage, this._latched);
|
|
|
|
}
|
|
|
|
|
2022-04-16 11:06:09 +00:00
|
|
|
_setModifierEnabled(keyval, enabled) {
|
|
|
|
if (enabled)
|
|
|
|
this._modifiers.add(keyval);
|
|
|
|
else
|
|
|
|
this._modifiers.delete(keyval);
|
|
|
|
|
|
|
|
for (const key of this._modifierKeys[keyval])
|
|
|
|
key.setLatched(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
_toggleModifier(keyval) {
|
|
|
|
const isActive = this._modifiers.has(keyval);
|
|
|
|
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() {
|
|
|
|
for (const keyval of this._modifiers)
|
|
|
|
this._setModifierEnabled(keyval, false);
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_popupLanguageMenu(keyActor) {
|
2018-01-23 15:45:46 +00:00
|
|
|
if (this._languagePopup)
|
|
|
|
this._languagePopup.destroy();
|
|
|
|
|
|
|
|
this._languagePopup = new LanguageSelectionPopup(keyActor);
|
2019-06-18 22:24:01 +00:00
|
|
|
Main.layoutManager.addTopChrome(this._languagePopup.actor);
|
2018-01-23 15:45:46 +00:00
|
|
|
this._languagePopup.open(true);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-01-23 15:45:46 +00:00
|
|
|
|
2019-01-19 14:58:45 +00:00
|
|
|
_updateCurrentPageVisible() {
|
2019-05-09 19:37:20 +00:00
|
|
|
if (this._currentPage)
|
|
|
|
this._currentPage.visible = !this._emojiActive && !this._keypadVisible;
|
2019-01-19 14:58:45 +00:00
|
|
|
}
|
|
|
|
|
2017-12-22 15:02:51 +00:00
|
|
|
_setEmojiActive(active) {
|
|
|
|
this._emojiActive = active;
|
2019-07-16 09:24:13 +00:00
|
|
|
this._emojiSelection.visible = this._emojiActive;
|
2019-01-19 14:58:45 +00:00
|
|
|
this._updateCurrentPageVisible();
|
2017-12-22 15:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_toggleEmoji() {
|
|
|
|
this._setEmojiActive(!this._emojiActive);
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_setCurrentLevelLatched(layout, latched) {
|
2019-01-21 20:41:46 +00:00
|
|
|
for (let i = 0; i < layout.shiftKeys.length; i++) {
|
2018-02-15 13:48:46 +00:00
|
|
|
let key = layout.shiftKeys[i];
|
|
|
|
key.setLatched(latched);
|
2022-04-16 11:06:09 +00:00
|
|
|
key.iconName = latched
|
|
|
|
? 'keyboard-caps-lock-symbolic' : 'keyboard-shift-symbolic';
|
2018-02-15 13:48:46 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2018-02-15 13:48:46 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_loadRows(model, level, numLevels, layout) {
|
2017-08-05 10:21:30 +00:00
|
|
|
let rows = model.rows;
|
2011-08-29 15:11:22 +00:00
|
|
|
for (let i = 0; i < rows.length; ++i) {
|
2017-12-20 12:08:50 +00:00
|
|
|
layout.appendRow();
|
2022-04-15 11:10:45 +00:00
|
|
|
this._addRowKeys(rows[i], layout);
|
2017-12-05 21:41:32 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-05 21:41:32 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_getGridSlots() {
|
2017-12-05 21:41:32 +00:00
|
|
|
let numOfHorizSlots = 0, numOfVertSlots;
|
2019-05-09 19:37:20 +00:00
|
|
|
let rows = this._currentPage.get_children();
|
2017-12-05 21:41:32 +00:00
|
|
|
numOfVertSlots = rows.length;
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2017-12-05 21:41:32 +00:00
|
|
|
for (let i = 0; i < rows.length; ++i) {
|
2019-01-31 13:43:52 +00:00
|
|
|
let keyboardRow = rows[i];
|
|
|
|
let keys = keyboardRow.get_children();
|
2017-12-05 21:41:32 +00:00
|
|
|
|
|
|
|
numOfHorizSlots = Math.max(numOfHorizSlots, keys.length);
|
2011-08-29 15:11:22 +00:00
|
|
|
}
|
|
|
|
|
2017-12-05 21:41:32 +00:00
|
|
|
return [numOfHorizSlots, numOfVertSlots];
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_relayout() {
|
2012-10-14 16:57:45 +00:00
|
|
|
let monitor = Main.layoutManager.keyboardMonitor;
|
2017-10-11 10:59:19 +00:00
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
if (!monitor)
|
2017-10-11 10:59:19 +00:00
|
|
|
return;
|
|
|
|
|
2019-07-16 09:24:13 +00:00
|
|
|
this.width = monitor.width;
|
2020-01-21 16:01:56 +00:00
|
|
|
|
2022-06-01 15:39:57 +00:00
|
|
|
if (monitor.width > monitor.height)
|
|
|
|
this.height = monitor.height / 3;
|
|
|
|
else
|
|
|
|
this.height = monitor.height / 4;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2022-04-22 19:16:52 +00:00
|
|
|
_updateKeys() {
|
2018-01-23 16:03:02 +00:00
|
|
|
this._ensureKeysForGroup(this._keyboardController.getCurrentGroup());
|
2017-08-05 10:21:30 +00:00
|
|
|
this._setActiveLayer(0);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2022-04-22 19:16:52 +00:00
|
|
|
_onGroupChanged() {
|
|
|
|
this._updateKeys();
|
|
|
|
}
|
|
|
|
|
|
|
|
_onTerminalModeChanged() {
|
|
|
|
this._updateKeys();
|
|
|
|
}
|
|
|
|
|
2019-02-04 11:30:53 +00:00
|
|
|
_onKeyboardGroupsChanged() {
|
2019-07-16 09:24:13 +00:00
|
|
|
let nonGroupActors = [this._emojiSelection, this._keypad];
|
2019-04-30 12:08:25 +00:00
|
|
|
this._aspectContainer.get_children().filter(c => !nonGroupActors.includes(c)).forEach(c => {
|
|
|
|
c.destroy();
|
|
|
|
});
|
|
|
|
|
|
|
|
this._groups = {};
|
2018-02-11 15:15:14 +00:00
|
|
|
this._onGroupChanged();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2013-02-18 03:01:04 +00:00
|
|
|
|
2019-01-19 14:58:45 +00:00
|
|
|
_onKeypadVisible(controller, visible) {
|
|
|
|
if (visible == this._keypadVisible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._keypadVisible = visible;
|
2019-07-16 09:24:13 +00:00
|
|
|
this._keypad.visible = this._keypadVisible;
|
2019-01-19 14:58:45 +00:00
|
|
|
this._updateCurrentPageVisible();
|
|
|
|
}
|
|
|
|
|
2017-12-22 15:02:51 +00:00
|
|
|
_onEmojiKeyVisible(controller, visible) {
|
|
|
|
if (visible == this._emojiKeyVisible)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._emojiKeyVisible = visible;
|
|
|
|
/* Rebuild keyboard widgetry to include emoji button */
|
|
|
|
this._onKeyboardGroupsChanged();
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onKeyboardStateChanged(controller, state) {
|
2017-12-05 19:05:18 +00:00
|
|
|
let enabled;
|
|
|
|
if (state == Clutter.InputPanelState.OFF)
|
|
|
|
enabled = false;
|
|
|
|
else if (state == Clutter.InputPanelState.ON)
|
|
|
|
enabled = true;
|
|
|
|
else if (state == Clutter.InputPanelState.TOGGLE)
|
2019-08-19 19:38:51 +00:00
|
|
|
enabled = this._keyboardVisible == false;
|
2017-12-05 19:05:18 +00:00
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (enabled)
|
2019-08-28 20:06:14 +00:00
|
|
|
this.open(Main.layoutManager.focusIndex);
|
2017-12-05 19:05:18 +00:00
|
|
|
else
|
2019-08-28 20:06:14 +00:00
|
|
|
this.close();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-05 19:05:18 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_setActiveLayer(activeLevel) {
|
2017-08-05 10:21:30 +00:00
|
|
|
let activeGroupName = this._keyboardController.getCurrentGroup();
|
|
|
|
let layers = this._groups[activeGroupName];
|
2019-05-09 19:35:11 +00:00
|
|
|
let currentPage = layers[activeLevel];
|
|
|
|
|
2019-05-09 19:37:20 +00:00
|
|
|
if (this._currentPage == currentPage) {
|
2019-05-09 19:35:11 +00:00
|
|
|
this._updateCurrentPageVisible();
|
|
|
|
return;
|
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2019-05-09 19:37:20 +00:00
|
|
|
if (this._currentPage != null) {
|
|
|
|
this._setCurrentLevelLatched(this._currentPage, false);
|
|
|
|
this._currentPage.disconnect(this._currentPage._destroyID);
|
|
|
|
this._currentPage.hide();
|
|
|
|
delete this._currentPage._destroyID;
|
2011-08-29 15:11:22 +00:00
|
|
|
}
|
|
|
|
|
2022-04-16 11:06:09 +00:00
|
|
|
this._disableAllModifiers();
|
2019-05-09 19:37:20 +00:00
|
|
|
this._currentPage = currentPage;
|
|
|
|
this._currentPage._destroyID = this._currentPage.connect('destroy', () => {
|
|
|
|
this._currentPage = null;
|
2019-05-09 19:30:21 +00:00
|
|
|
});
|
2019-01-19 14:58:45 +00:00
|
|
|
this._updateCurrentPageVisible();
|
2022-05-30 21:12:51 +00:00
|
|
|
this._aspectContainer.setRatio(...this._currentPage.getRatio());
|
2022-06-28 16:53:56 +00:00
|
|
|
this._emojiSelection.setRatio(...this._currentPage.getRatio());
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_clearKeyboardRestTimer() {
|
2012-11-18 15:17:21 +00:00
|
|
|
if (!this._keyboardRestingId)
|
|
|
|
return;
|
|
|
|
GLib.source_remove(this._keyboardRestingId);
|
|
|
|
this._keyboardRestingId = 0;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-11-18 15:17:21 +00:00
|
|
|
|
2021-02-11 23:54:55 +00:00
|
|
|
open(immediate = false) {
|
2017-01-16 15:36:21 +00:00
|
|
|
this._clearShowIdle();
|
2012-11-18 15:17:21 +00:00
|
|
|
this._keyboardRequested = true;
|
|
|
|
|
|
|
|
if (this._keyboardVisible) {
|
2021-02-11 15:44:28 +00:00
|
|
|
this._relayout();
|
2012-11-18 15:17:21 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-22 11:28:02 +00:00
|
|
|
this._oskCompletionEnabled =
|
2022-10-17 11:31:45 +00:00
|
|
|
IBusManager.getIBusManager().setCompletionEnabled(true, () => Main.inputMethod.update());
|
2021-02-20 11:33:29 +00:00
|
|
|
this._clearKeyboardRestTimer();
|
|
|
|
|
2021-02-11 23:54:55 +00:00
|
|
|
if (immediate) {
|
|
|
|
this._open();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-18 15:17:21 +00:00
|
|
|
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
2019-12-04 19:40:07 +00:00
|
|
|
KEYBOARD_REST_TIME,
|
|
|
|
() => {
|
|
|
|
this._clearKeyboardRestTimer();
|
2021-02-11 15:44:28 +00:00
|
|
|
this._open();
|
2019-12-04 19:40:07 +00:00
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
});
|
2014-04-10 17:26:52 +00:00
|
|
|
GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-11-18 15:17:21 +00:00
|
|
|
|
2021-02-11 15:44:28 +00:00
|
|
|
_open() {
|
2012-11-18 15:17:21 +00:00
|
|
|
if (!this._keyboardRequested)
|
|
|
|
return;
|
|
|
|
|
2017-12-20 12:08:50 +00:00
|
|
|
this._relayout();
|
2021-02-20 11:51:32 +00:00
|
|
|
this._animateShow();
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2017-12-22 15:02:51 +00:00
|
|
|
this._setEmojiActive(false);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2021-02-20 11:35:06 +00:00
|
|
|
close(immediate = false) {
|
2017-01-16 15:36:21 +00:00
|
|
|
this._clearShowIdle();
|
2012-11-18 15:17:21 +00:00
|
|
|
this._keyboardRequested = false;
|
|
|
|
|
|
|
|
if (!this._keyboardVisible)
|
|
|
|
return;
|
|
|
|
|
2022-10-17 11:31:45 +00:00
|
|
|
IBusManager.getIBusManager().setCompletionEnabled(false, () => Main.inputMethod.update());
|
2022-04-22 11:28:02 +00:00
|
|
|
this._oskCompletionEnabled = false;
|
2012-11-18 15:17:21 +00:00
|
|
|
this._clearKeyboardRestTimer();
|
2021-02-20 11:35:06 +00:00
|
|
|
|
|
|
|
if (immediate) {
|
|
|
|
this._close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-18 15:17:21 +00:00
|
|
|
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
|
2019-12-04 19:40:07 +00:00
|
|
|
KEYBOARD_REST_TIME,
|
|
|
|
() => {
|
|
|
|
this._clearKeyboardRestTimer();
|
|
|
|
this._close();
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
});
|
2014-04-10 17:26:52 +00:00
|
|
|
GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-11-18 15:17:21 +00:00
|
|
|
|
2019-08-28 20:06:14 +00:00
|
|
|
_close() {
|
2012-11-18 15:17:21 +00:00
|
|
|
if (this._keyboardRequested)
|
|
|
|
return;
|
|
|
|
|
2021-02-20 11:51:32 +00:00
|
|
|
this._animateHide();
|
2017-10-11 07:58:58 +00:00
|
|
|
this.setCursorLocation(null);
|
2022-04-16 11:06:09 +00:00
|
|
|
this._disableAllModifiers();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2021-02-20 11:51:32 +00:00
|
|
|
_animateShow() {
|
2021-02-11 14:36:42 +00:00
|
|
|
if (this._focusWindow)
|
|
|
|
this._animateWindow(this._focusWindow, true);
|
|
|
|
|
2021-02-11 12:08:47 +00:00
|
|
|
Main.layoutManager.keyboardBox.show();
|
|
|
|
this.ease({
|
|
|
|
translation_y: -this.height,
|
|
|
|
opacity: 255,
|
|
|
|
duration: KEYBOARD_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
this._animateShowComplete();
|
|
|
|
},
|
|
|
|
});
|
2021-02-11 13:10:48 +00:00
|
|
|
this._keyboardVisible = true;
|
|
|
|
this.emit('visibility-changed');
|
2021-02-11 12:08:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_animateShowComplete() {
|
|
|
|
let keyboardBox = Main.layoutManager.keyboardBox;
|
|
|
|
this._keyboardHeightNotifyId = keyboardBox.connect('notify::height', () => {
|
|
|
|
this.translation_y = -this.height;
|
|
|
|
});
|
|
|
|
|
2021-08-17 22:45:16 +00:00
|
|
|
// Toggle visibility so the keyboardBox can update its chrome region.
|
|
|
|
if (!Meta.is_wayland_compositor()) {
|
|
|
|
keyboardBox.hide();
|
|
|
|
keyboardBox.show();
|
|
|
|
}
|
2021-02-11 12:08:47 +00:00
|
|
|
}
|
|
|
|
|
2021-02-26 13:36:35 +00:00
|
|
|
_animateHide() {
|
2021-02-11 14:36:42 +00:00
|
|
|
if (this._focusWindow)
|
|
|
|
this._animateWindow(this._focusWindow, false);
|
|
|
|
|
2021-02-11 12:08:47 +00:00
|
|
|
if (this._keyboardHeightNotifyId) {
|
|
|
|
Main.layoutManager.keyboardBox.disconnect(this._keyboardHeightNotifyId);
|
|
|
|
this._keyboardHeightNotifyId = 0;
|
|
|
|
}
|
|
|
|
this.ease({
|
|
|
|
translation_y: 0,
|
|
|
|
opacity: 0,
|
2021-02-26 13:36:35 +00:00
|
|
|
duration: KEYBOARD_ANIMATION_TIME,
|
2021-02-11 12:08:47 +00:00
|
|
|
mode: Clutter.AnimationMode.EASE_IN_QUAD,
|
|
|
|
onComplete: () => {
|
|
|
|
this._animateHideComplete();
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2021-02-11 13:10:48 +00:00
|
|
|
this._keyboardVisible = false;
|
|
|
|
this.emit('visibility-changed');
|
2021-02-11 12:08:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_animateHideComplete() {
|
|
|
|
Main.layoutManager.keyboardBox.hide();
|
|
|
|
}
|
|
|
|
|
2021-02-11 23:54:55 +00:00
|
|
|
gestureProgress(delta) {
|
2021-02-14 09:26:38 +00:00
|
|
|
this._gestureInProgress = true;
|
2021-02-11 23:54:55 +00:00
|
|
|
Main.layoutManager.keyboardBox.show();
|
|
|
|
let progress = Math.min(delta, this.height) / this.height;
|
|
|
|
this.translation_y = -this.height * progress;
|
|
|
|
this.opacity = 255 * progress;
|
keyboard: Animate focus window using position instead of translation-y
Commit 8526776b4a53d9a801197fb66ce9a282323c1c4e changed the OSK to use
the translation-y property of the MetaWindowActor when animating a focus
window, which broke two things:
1) It's not compatible with the obscured region culling we do for
windows in mutter. That's because MetaCullable strictly operates in
integer coordinates and thus has to ignore any transformations
(translation-y is a transformation). Because of this, during the
animation and gesture, window damage is now tracked incorrectly,
introducing painting issues. The best fix for this would probably be
factoring in transformations when tracking damage in MetaCullable, but
that's not feasible right now.
2) It broke the shifting up of maximized and tiled windows, likely that
is because they are positioned using constraints internally, and mutter
enforces those constraints every time meta_window_move_frame() is
called, not allowing the window to move somewhere else.
To fix both issues, go back to the old way of shifting the window for
now, using the fixed y position of the ClutterActor. To make sure the
drag-up gesture still works, store the initial y1 position of the window
and then use that as a reference point for all our animations.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1760>
2021-02-20 11:55:58 +00:00
|
|
|
const windowActor = this._focusWindow?.get_compositor_private();
|
|
|
|
if (windowActor)
|
|
|
|
windowActor.y = this._focusWindowStartY - (this.height * progress);
|
2021-02-11 23:54:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
gestureActivate() {
|
|
|
|
this.open(true);
|
2021-02-14 09:26:38 +00:00
|
|
|
this._gestureInProgress = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
gestureCancel() {
|
|
|
|
if (this._gestureInProgress)
|
2021-02-20 11:51:32 +00:00
|
|
|
this._animateHide();
|
2021-02-14 09:26:38 +00:00
|
|
|
this._gestureInProgress = false;
|
2021-02-11 23:54:55 +00:00
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
resetSuggestions() {
|
2017-12-06 12:46:02 +00:00
|
|
|
if (this._suggestions)
|
|
|
|
this._suggestions.clear();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-06 12:46:02 +00:00
|
|
|
|
2022-06-29 11:06:07 +00:00
|
|
|
setSuggestionsVisible(visible) {
|
|
|
|
this._suggestions?.setVisible(visible);
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
addSuggestion(text, callback) {
|
2017-12-06 12:46:02 +00:00
|
|
|
if (!this._suggestions)
|
|
|
|
return;
|
|
|
|
this._suggestions.add(text, callback);
|
2019-07-16 09:24:13 +00:00
|
|
|
this._suggestions.show();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-06 12:46:02 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_clearShowIdle() {
|
2012-10-30 15:46:18 +00:00
|
|
|
if (!this._showIdleId)
|
|
|
|
return;
|
|
|
|
GLib.source_remove(this._showIdleId);
|
|
|
|
this._showIdleId = 0;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2012-10-30 15:46:18 +00:00
|
|
|
|
keyboard: Animate focus window using position instead of translation-y
Commit 8526776b4a53d9a801197fb66ce9a282323c1c4e changed the OSK to use
the translation-y property of the MetaWindowActor when animating a focus
window, which broke two things:
1) It's not compatible with the obscured region culling we do for
windows in mutter. That's because MetaCullable strictly operates in
integer coordinates and thus has to ignore any transformations
(translation-y is a transformation). Because of this, during the
animation and gesture, window damage is now tracked incorrectly,
introducing painting issues. The best fix for this would probably be
factoring in transformations when tracking damage in MetaCullable, but
that's not feasible right now.
2) It broke the shifting up of maximized and tiled windows, likely that
is because they are positioned using constraints internally, and mutter
enforces those constraints every time meta_window_move_frame() is
called, not allowing the window to move somewhere else.
To fix both issues, go back to the old way of shifting the window for
now, using the fixed y position of the ClutterActor. To make sure the
drag-up gesture still works, store the initial y1 position of the window
and then use that as a reference point for all our animations.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1760>
2021-02-20 11:55:58 +00:00
|
|
|
_windowSlideAnimationComplete(window, finalY) {
|
2019-01-31 14:04:56 +00:00
|
|
|
// Synchronize window positions again.
|
keyboard: Animate focus window using position instead of translation-y
Commit 8526776b4a53d9a801197fb66ce9a282323c1c4e changed the OSK to use
the translation-y property of the MetaWindowActor when animating a focus
window, which broke two things:
1) It's not compatible with the obscured region culling we do for
windows in mutter. That's because MetaCullable strictly operates in
integer coordinates and thus has to ignore any transformations
(translation-y is a transformation). Because of this, during the
animation and gesture, window damage is now tracked incorrectly,
introducing painting issues. The best fix for this would probably be
factoring in transformations when tracking damage in MetaCullable, but
that's not feasible right now.
2) It broke the shifting up of maximized and tiled windows, likely that
is because they are positioned using constraints internally, and mutter
enforces those constraints every time meta_window_move_frame() is
called, not allowing the window to move somewhere else.
To fix both issues, go back to the old way of shifting the window for
now, using the fixed y position of the ClutterActor. To make sure the
drag-up gesture still works, store the initial y1 position of the window
and then use that as a reference point for all our animations.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1760>
2021-02-20 11:55:58 +00:00
|
|
|
const frameRect = window.get_frame_rect();
|
|
|
|
const bufferRect = window.get_buffer_rect();
|
2021-02-11 23:53:21 +00:00
|
|
|
|
keyboard: Animate focus window using position instead of translation-y
Commit 8526776b4a53d9a801197fb66ce9a282323c1c4e changed the OSK to use
the translation-y property of the MetaWindowActor when animating a focus
window, which broke two things:
1) It's not compatible with the obscured region culling we do for
windows in mutter. That's because MetaCullable strictly operates in
integer coordinates and thus has to ignore any transformations
(translation-y is a transformation). Because of this, during the
animation and gesture, window damage is now tracked incorrectly,
introducing painting issues. The best fix for this would probably be
factoring in transformations when tracking damage in MetaCullable, but
that's not feasible right now.
2) It broke the shifting up of maximized and tiled windows, likely that
is because they are positioned using constraints internally, and mutter
enforces those constraints every time meta_window_move_frame() is
called, not allowing the window to move somewhere else.
To fix both issues, go back to the old way of shifting the window for
now, using the fixed y position of the ClutterActor. To make sure the
drag-up gesture still works, store the initial y1 position of the window
and then use that as a reference point for all our animations.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1760>
2021-02-20 11:55:58 +00:00
|
|
|
finalY += frameRect.y - bufferRect.y;
|
|
|
|
|
|
|
|
frameRect.y = finalY;
|
2021-02-26 15:08:20 +00:00
|
|
|
|
|
|
|
this._focusTracker.disconnect(this._windowMovedId);
|
2017-10-11 07:58:58 +00:00
|
|
|
window.move_frame(true, frameRect.x, frameRect.y);
|
2021-02-26 15:08:20 +00:00
|
|
|
this._windowMovedId = this._focusTracker.connect('window-moved',
|
|
|
|
this._onFocusWindowMoving.bind(this));
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-10-11 07:58:58 +00:00
|
|
|
|
2018-05-25 09:35:49 +00:00
|
|
|
_animateWindow(window, show) {
|
2017-10-11 07:58:58 +00:00
|
|
|
let windowActor = window.get_compositor_private();
|
|
|
|
if (!windowActor)
|
2011-09-27 18:55:38 +00:00
|
|
|
return;
|
|
|
|
|
keyboard: Animate focus window using position instead of translation-y
Commit 8526776b4a53d9a801197fb66ce9a282323c1c4e changed the OSK to use
the translation-y property of the MetaWindowActor when animating a focus
window, which broke two things:
1) It's not compatible with the obscured region culling we do for
windows in mutter. That's because MetaCullable strictly operates in
integer coordinates and thus has to ignore any transformations
(translation-y is a transformation). Because of this, during the
animation and gesture, window damage is now tracked incorrectly,
introducing painting issues. The best fix for this would probably be
factoring in transformations when tracking damage in MetaCullable, but
that's not feasible right now.
2) It broke the shifting up of maximized and tiled windows, likely that
is because they are positioned using constraints internally, and mutter
enforces those constraints every time meta_window_move_frame() is
called, not allowing the window to move somewhere else.
To fix both issues, go back to the old way of shifting the window for
now, using the fixed y position of the ClutterActor. To make sure the
drag-up gesture still works, store the initial y1 position of the window
and then use that as a reference point for all our animations.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1760>
2021-02-20 11:55:58 +00:00
|
|
|
const finalY = show
|
|
|
|
? this._focusWindowStartY - Main.layoutManager.keyboardBox.height
|
|
|
|
: this._focusWindowStartY;
|
|
|
|
|
|
|
|
windowActor.ease({
|
|
|
|
y: finalY,
|
|
|
|
duration: KEYBOARD_ANIMATION_TIME,
|
|
|
|
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
|
|
|
onStopped: () => {
|
|
|
|
windowActor.y = finalY;
|
|
|
|
this._windowSlideAnimationComplete(window, finalY);
|
|
|
|
},
|
|
|
|
});
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2011-08-29 15:11:22 +00:00
|
|
|
|
2021-02-26 15:08:20 +00:00
|
|
|
_onFocusWindowMoving() {
|
|
|
|
if (this._focusTracker.currentWindow === this._focusWindow) {
|
|
|
|
// Don't use _setFocusWindow() here because that would move the
|
|
|
|
// window while the user has grabbed it. Instead we simply "let go"
|
|
|
|
// of the window.
|
|
|
|
this._focusWindow = null;
|
|
|
|
this._focusWindowStartY = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.close(true);
|
|
|
|
}
|
|
|
|
|
2021-02-11 14:36:42 +00:00
|
|
|
_setFocusWindow(window) {
|
|
|
|
if (this._focusWindow === window)
|
2011-09-27 18:55:38 +00:00
|
|
|
return;
|
|
|
|
|
keyboard: Animate focus window using position instead of translation-y
Commit 8526776b4a53d9a801197fb66ce9a282323c1c4e changed the OSK to use
the translation-y property of the MetaWindowActor when animating a focus
window, which broke two things:
1) It's not compatible with the obscured region culling we do for
windows in mutter. That's because MetaCullable strictly operates in
integer coordinates and thus has to ignore any transformations
(translation-y is a transformation). Because of this, during the
animation and gesture, window damage is now tracked incorrectly,
introducing painting issues. The best fix for this would probably be
factoring in transformations when tracking damage in MetaCullable, but
that's not feasible right now.
2) It broke the shifting up of maximized and tiled windows, likely that
is because they are positioned using constraints internally, and mutter
enforces those constraints every time meta_window_move_frame() is
called, not allowing the window to move somewhere else.
To fix both issues, go back to the old way of shifting the window for
now, using the fixed y position of the ClutterActor. To make sure the
drag-up gesture still works, store the initial y1 position of the window
and then use that as a reference point for all our animations.
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1760>
2021-02-20 11:55:58 +00:00
|
|
|
if (this._keyboardVisible && this._focusWindow)
|
|
|
|
this._animateWindow(this._focusWindow, false);
|
|
|
|
|
|
|
|
const windowActor = window?.get_compositor_private();
|
|
|
|
windowActor?.remove_transition('y');
|
|
|
|
this._focusWindowStartY = windowActor ? windowActor.y : null;
|
|
|
|
|
|
|
|
if (this._keyboardVisible && window)
|
|
|
|
this._animateWindow(window, true);
|
2017-10-11 07:58:58 +00:00
|
|
|
|
2021-02-11 14:36:42 +00:00
|
|
|
this._focusWindow = window;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-10-11 07:58:58 +00:00
|
|
|
|
2019-01-29 01:27:05 +00:00
|
|
|
setCursorLocation(window, x, y, w, h) {
|
2018-05-25 09:35:49 +00:00
|
|
|
let monitor = Main.layoutManager.keyboardMonitor;
|
2017-10-11 07:58:58 +00:00
|
|
|
|
2018-05-25 09:35:49 +00:00
|
|
|
if (window && monitor) {
|
2021-02-20 12:58:39 +00:00
|
|
|
const keyboardHeight = Main.layoutManager.keyboardBox.height;
|
|
|
|
const keyboardY1 = (monitor.y + monitor.height) - keyboardHeight;
|
2017-10-11 07:58:58 +00:00
|
|
|
|
2021-02-20 12:58:39 +00:00
|
|
|
if (this._focusWindow === window) {
|
|
|
|
if (y + h + keyboardHeight < keyboardY1)
|
|
|
|
this._setFocusWindow(null);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (y + h >= keyboardY1)
|
2021-02-11 14:36:42 +00:00
|
|
|
this._setFocusWindow(window);
|
2021-02-20 12:58:39 +00:00
|
|
|
else
|
2021-02-11 14:36:42 +00:00
|
|
|
this._setFocusWindow(null);
|
2018-05-25 09:35:49 +00:00
|
|
|
} else {
|
2021-02-11 14:36:42 +00:00
|
|
|
this._setFocusWindow(null);
|
2017-10-11 07:58:58 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2019-07-16 09:24:13 +00:00
|
|
|
});
|
2015-04-02 22:18:07 +00:00
|
|
|
|
2022-07-04 22:30:44 +00:00
|
|
|
var KeyboardController = class extends Signals.EventEmitter {
|
2017-10-31 01:19:44 +00:00
|
|
|
constructor() {
|
2022-07-04 22:30:44 +00:00
|
|
|
super();
|
|
|
|
|
2019-10-05 10:35:21 +00:00
|
|
|
let seat = Clutter.get_default_backend().get_default_seat();
|
|
|
|
this._virtualDevice = seat.create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE);
|
2016-07-20 15:50:55 +00:00
|
|
|
|
|
|
|
this._inputSourceManager = InputSourceManager.getInputSourceManager();
|
2021-08-15 22:36:59 +00:00
|
|
|
this._inputSourceManager.connectObject(
|
|
|
|
'current-source-changed', this._onSourceChanged.bind(this),
|
|
|
|
'sources-changed', this._onSourcesModified.bind(this), this);
|
2017-08-05 10:21:30 +00:00
|
|
|
this._currentSource = this._inputSourceManager.currentSource;
|
2017-12-05 19:05:18 +00:00
|
|
|
|
2021-08-15 22:36:59 +00:00
|
|
|
Main.inputMethod.connectObject(
|
|
|
|
'notify::content-purpose', this._onContentPurposeHintsChanged.bind(this),
|
|
|
|
'notify::content-hints', this._onContentPurposeHintsChanged.bind(this),
|
|
|
|
'input-panel-state', (o, state) => this.emit('panel-state', state), this);
|
2020-02-18 14:05:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
destroy() {
|
2021-08-15 22:36:59 +00:00
|
|
|
this._inputSourceManager.disconnectObject(this);
|
|
|
|
Main.inputMethod.disconnectObject(this);
|
2020-03-28 10:54:32 +00:00
|
|
|
|
|
|
|
// Make sure any buttons pressed by the virtual device are released
|
|
|
|
// immediately instead of waiting for the next GC cycle
|
|
|
|
this._virtualDevice.run_dispose();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-07-20 15:50:55 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onSourcesModified() {
|
2017-08-05 10:21:30 +00:00
|
|
|
this.emit('groups-changed');
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-07-20 15:50:55 +00:00
|
|
|
|
2019-01-31 14:08:10 +00:00
|
|
|
_onSourceChanged(inputSourceManager, _oldSource) {
|
2016-07-20 15:50:55 +00:00
|
|
|
let source = inputSourceManager.currentSource;
|
2017-08-05 10:21:30 +00:00
|
|
|
this._currentSource = source;
|
|
|
|
this.emit('active-group', source.id);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-07-20 15:50:55 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onContentPurposeHintsChanged(method) {
|
2017-12-05 19:05:18 +00:00
|
|
|
let purpose = method.content_purpose;
|
2017-12-22 15:02:51 +00:00
|
|
|
let emojiVisible = false;
|
2019-01-19 14:58:45 +00:00
|
|
|
let keypadVisible = false;
|
2017-12-22 15:02:51 +00:00
|
|
|
|
|
|
|
if (purpose == Clutter.InputContentPurpose.NORMAL ||
|
|
|
|
purpose == Clutter.InputContentPurpose.ALPHA ||
|
|
|
|
purpose == Clutter.InputContentPurpose.PASSWORD ||
|
|
|
|
purpose == Clutter.InputContentPurpose.TERMINAL)
|
|
|
|
emojiVisible = true;
|
2019-01-19 14:58:45 +00:00
|
|
|
if (purpose == Clutter.InputContentPurpose.DIGITS ||
|
|
|
|
purpose == Clutter.InputContentPurpose.NUMBER ||
|
|
|
|
purpose == Clutter.InputContentPurpose.PHONE)
|
|
|
|
keypadVisible = true;
|
2017-12-22 15:02:51 +00:00
|
|
|
|
2019-01-29 01:18:52 +00:00
|
|
|
this.emit('emoji-visible', emojiVisible);
|
2019-01-19 14:58:45 +00:00
|
|
|
this.emit('keypad-visible', keypadVisible);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-05 19:05:18 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
getGroups() {
|
2016-07-20 15:50:55 +00:00
|
|
|
let inputSources = this._inputSourceManager.inputSources;
|
2019-01-29 01:18:52 +00:00
|
|
|
let groups = [];
|
2016-07-20 15:50:55 +00:00
|
|
|
|
|
|
|
for (let i in inputSources) {
|
|
|
|
let is = inputSources[i];
|
2017-08-05 10:21:30 +00:00
|
|
|
groups[is.index] = is.xkbId;
|
2016-07-20 15:50:55 +00:00
|
|
|
}
|
|
|
|
|
2017-08-05 10:21:30 +00:00
|
|
|
return groups;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-08-05 10:21:30 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
getCurrentGroup() {
|
2022-04-22 19:16:52 +00:00
|
|
|
if (Main.inputMethod.terminalMode)
|
|
|
|
return 'us-extended';
|
2022-06-15 13:49:19 +00:00
|
|
|
|
|
|
|
// Special case for Korean, if Hangul mode is disabled, use the 'us' keymap
|
|
|
|
if (this._currentSource.id === 'hangul') {
|
|
|
|
const inputSourceManager = InputSourceManager.getInputSourceManager();
|
|
|
|
const currentSource = inputSourceManager.currentSource;
|
|
|
|
let prop;
|
|
|
|
for (let i = 0; (prop = currentSource.properties.get(i)) !== null; ++i) {
|
|
|
|
if (prop.get_key() === 'InputMode' &&
|
|
|
|
prop.get_prop_type() === IBus.PropType.TOGGLE &&
|
|
|
|
prop.get_state() !== IBus.PropState.CHECKED)
|
|
|
|
return 'us';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-05 10:21:30 +00:00
|
|
|
return this._currentSource.xkbId;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2016-07-20 15:50:55 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
commitString(string, fromKey) {
|
2017-12-05 19:05:18 +00:00
|
|
|
if (string == null)
|
|
|
|
return false;
|
|
|
|
/* Let ibus methods fall through keyval emission */
|
|
|
|
if (fromKey && this._currentSource.type == InputSourceManager.INPUT_SOURCE_TYPE_IBUS)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Main.inputMethod.commit(string);
|
|
|
|
return true;
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2017-12-05 19:05:18 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
keyvalPress(keyval) {
|
2021-07-22 15:24:30 +00:00
|
|
|
this._virtualDevice.notify_keyval(Clutter.get_current_event_time() * 1000,
|
2016-07-20 15:50:55 +00:00
|
|
|
keyval, Clutter.KeyState.PRESSED);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2015-04-02 22:18:07 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
keyvalRelease(keyval) {
|
2021-07-22 15:24:30 +00:00
|
|
|
this._virtualDevice.notify_keyval(Clutter.get_current_event_time() * 1000,
|
2016-07-20 15:50:55 +00:00
|
|
|
keyval, Clutter.KeyState.RELEASED);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
|
|
|
};
|