misc: Add InputMethod class
This is a ClutterInputMethod implementation using IBus underneath. The input method will interact with the currently focused ClutterInputFocus, be it shell chrome or wayland clients through the text_input protocol.
This commit is contained in:
parent
774930f1a1
commit
5914f225a2
@ -17,6 +17,7 @@
|
|||||||
<file>misc/gnomeSession.js</file>
|
<file>misc/gnomeSession.js</file>
|
||||||
<file>misc/history.js</file>
|
<file>misc/history.js</file>
|
||||||
<file>misc/ibusManager.js</file>
|
<file>misc/ibusManager.js</file>
|
||||||
|
<file>misc/inputMethod.js</file>
|
||||||
<file>misc/jsParse.js</file>
|
<file>misc/jsParse.js</file>
|
||||||
<file>misc/keyboardManager.js</file>
|
<file>misc/keyboardManager.js</file>
|
||||||
<file>misc/loginManager.js</file>
|
<file>misc/loginManager.js</file>
|
||||||
|
214
js/misc/inputMethod.js
Normal file
214
js/misc/inputMethod.js
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||||
|
const Clutter = imports.gi.Clutter;
|
||||||
|
const IBus = imports.gi.IBus;
|
||||||
|
const Keyboard = imports.ui.status.keyboard;
|
||||||
|
const Lang = imports.lang;
|
||||||
|
const Signals = imports.signals;
|
||||||
|
|
||||||
|
var InputMethod = new Lang.Class({
|
||||||
|
Name: 'InputMethod',
|
||||||
|
Extends: Clutter.InputMethod,
|
||||||
|
|
||||||
|
_init: function() {
|
||||||
|
this.parent();
|
||||||
|
this._hints = 0;
|
||||||
|
this._purpose = 0;
|
||||||
|
this._enabled = true;
|
||||||
|
this._currentFocus = null;
|
||||||
|
this._ibus = IBus.Bus.new_async();
|
||||||
|
this._ibus.connect('connected', Lang.bind(this, this._onConnected));
|
||||||
|
this._ibus.connect('disconnected', Lang.bind(this, this._clear));
|
||||||
|
this.connect('notify::can-show-preedit', Lang.bind(this, this._updateCapabilities));
|
||||||
|
|
||||||
|
this._inputSourceManager = Keyboard.getInputSourceManager();
|
||||||
|
this._sourceChangedId = this._inputSourceManager.connect('current-source-changed',
|
||||||
|
Lang.bind(this, this._onSourceChanged));
|
||||||
|
this._currentSource = this._inputSourceManager.currentSource;
|
||||||
|
|
||||||
|
if (this._ibus.is_connected())
|
||||||
|
this._onConnected();
|
||||||
|
},
|
||||||
|
|
||||||
|
get currentFocus() {
|
||||||
|
return this._currentFocus;
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateCapabilities: function() {
|
||||||
|
let caps = 0;
|
||||||
|
|
||||||
|
if (this.can_show_preedit)
|
||||||
|
caps |= IBus.Capabilite.PREEDIT_TEXT;
|
||||||
|
|
||||||
|
if (this._currentFocus)
|
||||||
|
caps |= IBus.Capabilite.FOCUS | IBus.Capabilite.SURROUNDING_TEXT;
|
||||||
|
else
|
||||||
|
caps |= IBus.Capabilite.PREEDIT_TEXT | IBus.Capabilite.AUXILIARY_TEXT | IBus.Capabilite.LOOKUP_TABLE | IBus.Capabilite.PROPERTY;
|
||||||
|
|
||||||
|
if (this._context)
|
||||||
|
this._context.set_capabilities(caps);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSourceChanged: function() {
|
||||||
|
this._currentSource = this._inputSourceManager.currentSource;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onConnected: function() {
|
||||||
|
this._ibus.create_input_context_async ('gnome-shell', -1, null,
|
||||||
|
Lang.bind(this, this._setContext));
|
||||||
|
},
|
||||||
|
|
||||||
|
_setContext: function(bus, res) {
|
||||||
|
this._context = this._ibus.create_input_context_async_finish(res);
|
||||||
|
this._context.connect('enabled', Lang.bind(this, function () { this._enabled = true }));
|
||||||
|
this._context.connect('disabled', Lang.bind(this, function () { this._enabled = false }));
|
||||||
|
this._context.connect('commit-text', Lang.bind(this, this._onCommitText));
|
||||||
|
this._context.connect('delete-surrounding-text', Lang.bind(this, this._onDeleteSurroundingText));
|
||||||
|
this._context.connect('update-preedit-text', Lang.bind(this, this._onUpdatePreeditText));
|
||||||
|
|
||||||
|
this._updateCapabilities();
|
||||||
|
},
|
||||||
|
|
||||||
|
_clear: function() {
|
||||||
|
this._context = null;
|
||||||
|
this._hints = 0;
|
||||||
|
this._purpose = 0;
|
||||||
|
this._enabled = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
_emitRequestSurrounding: function() {
|
||||||
|
if (this._context.needs_surrounding_text())
|
||||||
|
this.emit('request-surrounding');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCommitText: function(context, text) {
|
||||||
|
this.commit(text.get_text());
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDeleteSurroundingText: function (context) {
|
||||||
|
this.delete_surrounding();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onUpdatePreeditText: function (context, text, pos, visible) {
|
||||||
|
let str = null;
|
||||||
|
if (visible && text != null)
|
||||||
|
str = text.get_text();
|
||||||
|
|
||||||
|
this.set_preedit_text(str, pos);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_focus_in: function(focus) {
|
||||||
|
this._currentFocus = focus;
|
||||||
|
if (this._context) {
|
||||||
|
this._context.focus_in();
|
||||||
|
this._updateCapabilities();
|
||||||
|
this._emitRequestSurrounding();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_focus_out: function() {
|
||||||
|
this._currentFocus = null;
|
||||||
|
if (this._context) {
|
||||||
|
this._context.focus_out();
|
||||||
|
this._updateCapabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset any preedit text
|
||||||
|
this.set_preedit_text(null, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_reset: function() {
|
||||||
|
if (this._context) {
|
||||||
|
this._context.reset();
|
||||||
|
this._emitRequestSurrounding();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unset any preedit text
|
||||||
|
this.set_preedit_text(null, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_set_cursor_location: function(rect) {
|
||||||
|
if (this._context) {
|
||||||
|
this._context.set_cursor_location(rect.get_x(), rect.get_y(),
|
||||||
|
rect.get_width(), rect.get_height());
|
||||||
|
this._emitRequestSurrounding();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_set_surrounding: function(text, cursor, anchor) {
|
||||||
|
if (this._context)
|
||||||
|
this._context.set_surrounding_text(text, cursor, anchor);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_update_content_hints: function(hints) {
|
||||||
|
let ibusHints = 0;
|
||||||
|
if (hints & Clutter.InputContentHintFlags.COMPLETION)
|
||||||
|
ibusHints |= IBus.InputHints.WORD_COMPLETION;
|
||||||
|
if (hints & Clutter.InputContentHintFlags.SPELLCHECK)
|
||||||
|
ibusHints |= IBus.InputHints.SPELLCHECK;
|
||||||
|
if (hints & Clutter.InputContentHintFlags.AUTO_CAPITALIZATION)
|
||||||
|
ibusHints |= IBus.InputHints.UPPERCASE_SENTENCES;
|
||||||
|
if (hints & Clutter.InputContentHintFlags.LOWERCASE)
|
||||||
|
ibusHints |= IBus.InputHints.LOWERCASE;
|
||||||
|
if (hints & Clutter.InputContentHintFlags.UPPERCASE)
|
||||||
|
ibusHints |= IBus.InputHints.UPPERCASE_CHARS;
|
||||||
|
if (hints & Clutter.InputContentHintFlags.TITLECASE)
|
||||||
|
ibusHints |= IBus.InputHints.UPPERCASE_WORDS;
|
||||||
|
|
||||||
|
this._hints = ibusHints;
|
||||||
|
if (this._context)
|
||||||
|
this._context.set_content_type(this._purpose, this._hints);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_update_content_purpose: function(purpose) {
|
||||||
|
let ibusPurpose = 0;
|
||||||
|
if (purpose == Clutter.InputContentPurpose.NORMAL)
|
||||||
|
ibusPurpose = IBus.InputPurpose.FREE_FORM;
|
||||||
|
else if (purpose == Clutter.InputContentPurpose.ALPHA)
|
||||||
|
ibusPurpose = IBus.InputPurpose.ALPHA;
|
||||||
|
else if (purpose == Clutter.InputContentPurpose.DIGITS)
|
||||||
|
ibusPurpose = IBus.InputPurpose.DIGITS;
|
||||||
|
else if (purpose == Clutter.InputContentPurpose.NUMBER)
|
||||||
|
ibusPurpose = IBus.InputPurpose.NUMBER;
|
||||||
|
else if (purpose == Clutter.InputContentPurpose.PHONE)
|
||||||
|
ibusPurpose = IBus.InputPurpose.PHONE;
|
||||||
|
else if (purpose == Clutter.InputContentPurpose.URL)
|
||||||
|
ibusPurpose = IBus.InputPurpose.URL;
|
||||||
|
else if (purpose == Clutter.InputContentPurpose.EMAIL)
|
||||||
|
ibusPurpose = IBus.InputPurpose.EMAIL;
|
||||||
|
else if (purpose == Clutter.InputContentPurpose.NAME)
|
||||||
|
ibusPurpose = IBus.InputPurpose.NAME;
|
||||||
|
else if (purpose == Clutter.InputContentPurpose.PASSWORD)
|
||||||
|
ibusPurpose = IBus.InputPurpose.PASSWORD;
|
||||||
|
|
||||||
|
this._purpose = ibusPurpose;
|
||||||
|
if (this._context)
|
||||||
|
this._context.set_content_type(this._purpose, this._hints);
|
||||||
|
},
|
||||||
|
|
||||||
|
vfunc_filter_key_event: function(event) {
|
||||||
|
if (!this._context || !this._enabled)
|
||||||
|
return false;
|
||||||
|
if (!this._currentSource ||
|
||||||
|
this._currentSource.type == Keyboard.INPUT_SOURCE_TYPE_XKB)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let state = event.get_state();
|
||||||
|
if (state & IBus.ModifierType.IGNORED_MASK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (event.type() == Clutter.EventType.KEY_RELEASE)
|
||||||
|
state |= IBus.ModifierType.RELEASE_MASK;
|
||||||
|
this._context.process_key_event_async(event.get_key_symbol(),
|
||||||
|
event.get_key_code() - 8, // Convert XKB keycodes to evcodes
|
||||||
|
state, -1, null,
|
||||||
|
Lang.bind(this, (context, res) => {
|
||||||
|
try {
|
||||||
|
let retval = context.process_key_event_async_finish(res);
|
||||||
|
this.notify_key_event(event, retval);
|
||||||
|
} catch (e) {
|
||||||
|
log('Error processing key on IM: ' + e.message);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
@ -19,6 +19,7 @@ const EndSessionDialog = imports.ui.endSessionDialog;
|
|||||||
const Environment = imports.ui.environment;
|
const Environment = imports.ui.environment;
|
||||||
const ExtensionSystem = imports.ui.extensionSystem;
|
const ExtensionSystem = imports.ui.extensionSystem;
|
||||||
const ExtensionDownloader = imports.ui.extensionDownloader;
|
const ExtensionDownloader = imports.ui.extensionDownloader;
|
||||||
|
const InputMethod = imports.misc.inputMethod;
|
||||||
const Keyboard = imports.ui.keyboard;
|
const Keyboard = imports.ui.keyboard;
|
||||||
const MessageTray = imports.ui.messageTray;
|
const MessageTray = imports.ui.messageTray;
|
||||||
const ModalDialog = imports.ui.modalDialog;
|
const ModalDialog = imports.ui.modalDialog;
|
||||||
@ -80,6 +81,7 @@ var xdndHandler = null;
|
|||||||
var keyboard = null;
|
var keyboard = null;
|
||||||
var layoutManager = null;
|
var layoutManager = null;
|
||||||
var kbdA11yDialog = null;
|
var kbdA11yDialog = null;
|
||||||
|
var inputMethod = null;
|
||||||
let _startDate;
|
let _startDate;
|
||||||
let _defaultCssStylesheet = null;
|
let _defaultCssStylesheet = null;
|
||||||
let _cssStylesheet = null;
|
let _cssStylesheet = null;
|
||||||
@ -173,6 +175,9 @@ function _initializeUI() {
|
|||||||
if (LoginManager.canLock())
|
if (LoginManager.canLock())
|
||||||
screenShield = new ScreenShield.ScreenShield();
|
screenShield = new ScreenShield.ScreenShield();
|
||||||
|
|
||||||
|
inputMethod = new InputMethod.InputMethod();
|
||||||
|
Clutter.get_default_backend().set_input_method(inputMethod);
|
||||||
|
|
||||||
messageTray = new MessageTray.MessageTray();
|
messageTray = new MessageTray.MessageTray();
|
||||||
panel = new Panel.Panel();
|
panel = new Panel.Panel();
|
||||||
keyboard = new Keyboard.Keyboard();
|
keyboard = new Keyboard.Keyboard();
|
||||||
|
Loading…
Reference in New Issue
Block a user