keyboard: Add KeyboardManager to manage the lifetime of the keyboard actor

The Keyboard class used to be both a view and controller class, however in order
to make the keyboard a native Clutter.Actor, we need to separate the widget from
the controller class, so that we can manage the actor lifetime from the JS side.

Thus, initialize the keyboard actor on the Keyboard constructor and create a
KeyboardManager class to manage its state and lifetime.

Add proxy methods for the public functions that were used by other shell
components

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/559
This commit is contained in:
Marco Trevisan (Treviño) 2019-08-28 23:52:52 +02:00 committed by Florian Müllner
parent c7e0c7eb79
commit 7059dcced3
2 changed files with 160 additions and 112 deletions

View File

@ -1,5 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Keyboard */ /* exported KeyboardManager */
const { Clutter, Gio, GLib, GObject, Meta, St } = imports.gi; const { Clutter, Gio, GLib, GObject, Meta, St } = imports.gi;
const Signals = imports.signals; const Signals = imports.signals;
@ -1042,76 +1042,23 @@ var Keypad = class Keypad {
}; };
Signals.addSignalMethods(Keypad.prototype); Signals.addSignalMethods(Keypad.prototype);
var Keyboard = class Keyboard { var KeyboardManager = class KeyBoardManager {
constructor() { constructor() {
this.actor = null; this._keyboard = null;
this._focusInExtendedKeys = false;
this._emojiActive = false;
this._languagePopup = null;
this._currentFocusWindow = null;
this._animFocusedWindow = null;
this._delayedAnimFocusWindow = null;
this._enableKeyboard = false; // a11y settings value
this._enabled = false; // enabled state (by setting or device type)
this._latched = false; // current level is latched
this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA }); this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA });
this._a11yApplicationsSettings.connect('changed', this._syncEnabled.bind(this)); this._a11yApplicationsSettings.connect('changed', this._syncEnabled.bind(this));
this._lastDeviceId = null; this._lastDeviceId = null;
this._suggestions = null; Meta.get_backend().connect('last-device-changed', (backend, deviceId) => {
this._emojiKeyVisible = Meta.is_wayland_compositor(); let manager = Clutter.DeviceManager.get_default();
let device = manager.get_device(deviceId);
this._focusTracker = new FocusTracker(); if (device.get_device_name().indexOf('XTEST') < 0) {
this._focusTracker.connect('position-changed', this._onFocusPositionChanged.bind(this)); this._lastDeviceId = deviceId;
this._focusTracker.connect('reset', () => { this._syncEnabled();
this._delayedAnimFocusWindow = null; }
this._animFocusedWindow = null;
this._oskFocusWindow = null;
}); });
this._focusTracker.connect('focus-changed', (tracker, focused) => {
// Valid only for X11
if (Meta.is_wayland_compositor())
return;
if (focused)
this.open(Main.layoutManager.focusIndex);
else
this.close();
});
Meta.get_backend().connect('last-device-changed',
(backend, deviceId) => {
let manager = Clutter.DeviceManager.get_default();
let device = manager.get_device(deviceId);
if (!device.get_device_name().includes('XTEST')) {
this._lastDeviceId = deviceId;
this._syncEnabled();
}
});
this._syncEnabled(); this._syncEnabled();
this._showIdleId = 0;
this._keyboardVisible = false;
Main.layoutManager.connect('keyboard-visible-changed', (o, visible) => {
this._keyboardVisible = visible;
});
this._keyboardRequested = false;
this._keyboardRestingId = 0;
Main.layoutManager.connect('monitors-changed', this._relayout.bind(this));
}
get visible() {
return this._keyboardVisible;
}
_onFocusPositionChanged(focusTracker) {
let rect = focusTracker.getCurrentRect();
this.setCursorLocation(focusTracker.currentWindow, rect.x, rect.y, rect.width, rect.height);
} }
_lastDeviceIsTouchscreen() { _lastDeviceIsTouchscreen() {
@ -1128,38 +1075,146 @@ var Keyboard = class Keyboard {
} }
_syncEnabled() { _syncEnabled() {
let wasEnabled = this._enabled; let enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD);
this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD); let enabled = enableKeyboard || this._lastDeviceIsTouchscreen();
this._enabled = this._enableKeyboard || this._lastDeviceIsTouchscreen(); if (!enabled && !this._keyboard)
if (!this._enabled && !this._keyboardController)
return; return;
if (this._enabled && !this._keyboardController) if (enabled && !this._keyboard) {
this._setupKeyboard(); this._keyboard = new Keyboard();
else if (!this._enabled) } else if (!enabled && this._keyboard) {
this.setCursorLocation(null); this._keyboard.setCursorLocation(null);
if (!this._enabled && wasEnabled)
Main.layoutManager.hideKeyboard(true); Main.layoutManager.hideKeyboard(true);
this._keyboard.destroy();
this._keyboard = null;
}
} }
_destroyKeyboard() { get keyboardActor() {
if (this._keyboardNotifyId) return this._keyboard.actor;
this._keyboardController.disconnect(this._keyboardNotifyId); }
if (this._keyboardGroupsChangedId)
this._keyboardController.disconnect(this._keyboardGroupsChangedId); get visible() {
if (this._keyboardStateId) return this._keyboard && this._keyboard.actor.visible;
this._keyboardController.disconnect(this._keyboardStateId); }
if (this._emojiKeyVisibleId)
this._keyboardController.disconnect(this._emojiKeyVisibleId); open(monitor) {
if (this._keypadVisibleId) if (this._keyboard)
this._keyboardController.disconnect(this._keypadVisibleId); this._keyboard.open(monitor);
if (this._focusNotifyId) }
global.stage.disconnect(this._focusNotifyId);
this._clearShowIdle(); close() {
this._keyboard = null; if (this._keyboard)
this._keyboard.close();
}
addSuggestion(text, callback) {
if (this._keyboard)
this._keyboard.addSuggestion(text, callback);
}
resetSuggestions() {
if (this._keyboard)
this._keyboard.resetSuggestions();
}
shouldTakeEvent(event) {
if (!this._keyboard)
return false;
let actor = event.get_source();
return Main.layoutManager.keyboardBox.contains(actor) ||
!!actor._extended_keys || !!actor.extended_key;
}
};
var Keyboard = class Keyboard {
constructor() {
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true });
this._focusInExtendedKeys = false;
this._emojiActive = false;
this._languagePopup = null;
this._currentFocusWindow = null;
this._animFocusedWindow = null;
this._delayedAnimFocusWindow = null;
this._latched = false; // current level is latched
this._suggestions = null;
this._emojiKeyVisible = Meta.is_wayland_compositor();
this._focusTracker = new FocusTracker();
this._connectSignal(this._focusTracker, 'position-changed',
this._onFocusPositionChanged.bind(this));
this._connectSignal(this._focusTracker, 'reset', () => {
this._delayedAnimFocusWindow = null;
this._animFocusedWindow = null;
this._oskFocusWindow = null;
});
// Valid only for X11
if (!Meta.is_wayland_compositor()) {
this._connectSignal(this._focusTracker, 'focus-changed', (_tracker, focused) => {
if (focused)
this.open(Main.layoutManager.focusIndex);
else
this.close();
});
}
this._showIdleId = 0;
this._keyboardVisible = false;
this._connectSignal(Main.layoutManager, 'keyboard-visible-changed', (_lm, visible) => {
this._keyboardVisible = visible;
});
this._keyboardRequested = false;
this._keyboardRestingId = 0;
this._connectSignal(Main.layoutManager, 'monitors-changed', this._relayout.bind(this));
this._setupKeyboard();
this.actor.connect('destroy', this._onDestroy.bind(this));
}
destroy() {
this.actor.destroy(); this.actor.destroy();
this.actor = null; }
_connectSignal(obj, signal, callback) {
if (!this._connectionsIDs)
this._connectionsIDs = [];
let id = obj.connect(signal, callback);
this._connectionsIDs.push([obj, id]);
return id;
}
get visible() {
return this._keyboardVisible && this.actor.visible;
}
_onFocusPositionChanged(focusTracker) {
let rect = focusTracker.getCurrentRect();
this.setCursorLocation(focusTracker.currentWindow, rect.x, rect.y, rect.width, rect.height);
}
_onDestroy() {
for (let [obj, id] of this._connectionsIDs)
obj.disconnect(id);
delete this._connectionsIDs;
this._clearShowIdle();
this._keyboardController = null;
this._suggestions = null;
this._aspectContainer = null;
this._emojiSelection = null;
this._keypad = null;
Main.layoutManager.untrackChrome(this.actor);
Main.layoutManager.keyboardBox.remove_actor(this.actor);
if (this._languagePopup) { if (this._languagePopup) {
this._languagePopup.destroy(); this._languagePopup.destroy();
@ -1168,7 +1223,6 @@ var Keyboard = class Keyboard {
} }
_setupKeyboard() { _setupKeyboard() {
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
Main.layoutManager.keyboardBox.add_actor(this.actor); Main.layoutManager.keyboardBox.add_actor(this.actor);
Main.layoutManager.trackChrome(this.actor); Main.layoutManager.trackChrome(this.actor);
@ -1196,7 +1250,7 @@ var Keyboard = class Keyboard {
this._emojiSelection.actor.hide(); this._emojiSelection.actor.hide();
this._keypad = new Keypad(); this._keypad = new Keypad();
this._keypad.connect('keyval', (keypad, keyval) => { this._connectSignal(this._keypad, 'keyval', (_keypad, keyval) => {
this._keyboardController.keyvalPress(keyval); this._keyboardController.keyvalPress(keyval);
this._keyboardController.keyvalRelease(keyval); this._keyboardController.keyvalRelease(keyval);
}); });
@ -1212,14 +1266,20 @@ var Keyboard = class Keyboard {
// keyboard on RTL locales. // keyboard on RTL locales.
this.actor.text_direction = Clutter.TextDirection.LTR; this.actor.text_direction = Clutter.TextDirection.LTR;
this._keyboardNotifyId = this._keyboardController.connect('active-group', this._onGroupChanged.bind(this)); this._connectSignal(this._keyboardController, 'active-group',
this._keyboardGroupsChangedId = this._keyboardController.connect('groups-changed', this._onKeyboardGroupsChanged.bind(this)); this._onGroupChanged.bind(this));
this._keyboardStateId = this._keyboardController.connect('panel-state', this._onKeyboardStateChanged.bind(this)); this._connectSignal(this._keyboardController, 'groups-changed',
this._keypadVisibleId = this._keyboardController.connect('keypad-visible', this._onKeypadVisible.bind(this)); this._onKeyboardGroupsChanged.bind(this));
this._focusNotifyId = global.stage.connect('notify::key-focus', this._onKeyFocusChanged.bind(this)); this._connectSignal(this._keyboardController, 'panel-state',
this._onKeyboardStateChanged.bind(this));
this._connectSignal(this._keyboardController, 'keypad-visible',
this._onKeypadVisible.bind(this));
this._connectSignal(global.stage, 'notify::key-focus',
this._onKeyFocusChanged.bind(this));
if (Meta.is_wayland_compositor()) if (Meta.is_wayland_compositor())
this._emojiKeyVisibleId = this._keyboardController.connect('emoji-visible', this._onEmojiKeyVisible.bind(this)); this._connectSignal(this._keyboardController, 'emoji-visible',
this._onEmojiKeyVisible.bind(this));
this._relayout(); this._relayout();
} }
@ -1245,7 +1305,7 @@ var Keyboard = class Keyboard {
this._showIdleId = 0; this._showIdleId = 0;
return GLib.SOURCE_REMOVE; return GLib.SOURCE_REMOVE;
}); });
GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.show'); GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.open');
} }
} }
@ -1552,12 +1612,6 @@ var Keyboard = class Keyboard {
this._updateCurrentPageVisible(); this._updateCurrentPageVisible();
} }
shouldTakeEvent(event) {
let actor = event.get_source();
return Main.layoutManager.keyboardBox.contains(actor) ||
!!actor._extended_keys || !!actor.extended_key;
}
_clearKeyboardRestTimer() { _clearKeyboardRestTimer() {
if (!this._keyboardRestingId) if (!this._keyboardRestingId)
return; return;
@ -1566,9 +1620,6 @@ var Keyboard = class Keyboard {
} }
open(monitor) { open(monitor) {
if (!this._enabled)
return;
this._clearShowIdle(); this._clearShowIdle();
this._keyboardRequested = true; this._keyboardRequested = true;
@ -1608,9 +1659,6 @@ var Keyboard = class Keyboard {
} }
close() { close() {
if (!this._enabled)
return;
this._clearShowIdle(); this._clearShowIdle();
this._keyboardRequested = false; this._keyboardRequested = false;

View File

@ -189,7 +189,7 @@ function _initializeUI() {
messageTray = new MessageTray.MessageTray(); messageTray = new MessageTray.MessageTray();
panel = new Panel.Panel(); panel = new Panel.Panel();
keyboard = new Keyboard.Keyboard(); keyboard = new Keyboard.KeyboardManager();
notificationDaemon = new NotificationDaemon.NotificationDaemon(); notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler(); windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
componentManager = new Components.ComponentManager(); componentManager = new Components.ComponentManager();