[gnome-shell/wip/carlosg/osk-cldr: 27/45] misc: Add InputMethod class



commit e5d3690960f1a56798c4ed3c898886ed94e5d6fd
Author: Carlos Garnacho <carlosg gnome org>
Date:   Tue Dec 5 20:05:00 2017 +0100

    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.

 js/js-resources.gresource.xml |   1 +
 js/misc/inputMethod.js        | 208 ++++++++++++++++++++++++++++++++++++++++++
 js/ui/main.js                 |   5 +
 3 files changed, 214 insertions(+)
---
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index ba0d2f483..6834bf608 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -17,6 +17,7 @@
     <file>misc/gnomeSession.js</file>
     <file>misc/history.js</file>
     <file>misc/ibusManager.js</file>
+    <file>misc/inputMethod.js</file>
     <file>misc/jsParse.js</file>
     <file>misc/keyboardManager.js</file>
     <file>misc/loginManager.js</file>
diff --git a/js/misc/inputMethod.js b/js/misc/inputMethod.js
new file mode 100644
index 000000000..5f8e8c7a5
--- /dev/null
+++ b/js/misc/inputMethod.js
@@ -0,0 +1,208 @@
+// -*- 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_client();
+        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();
+        }
+    },
+
+    vfunc_reset: function() {
+        if (this._context) {
+            this._context.reset();
+            this._emitRequestSurrounding();
+        }
+    },
+
+    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;
+    },
+});
diff --git a/js/ui/main.js b/js/ui/main.js
index 0f53edf48..7c1214e0a 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -19,6 +19,7 @@ const EndSessionDialog = imports.ui.endSessionDialog;
 const Environment = imports.ui.environment;
 const ExtensionSystem = imports.ui.extensionSystem;
 const ExtensionDownloader = imports.ui.extensionDownloader;
+const InputMethod = imports.misc.inputMethod;
 const Keyboard = imports.ui.keyboard;
 const MessageTray = imports.ui.messageTray;
 const ModalDialog = imports.ui.modalDialog;
@@ -80,6 +81,7 @@ var xdndHandler = null;
 var keyboard = null;
 var layoutManager = null;
 var kbdA11yDialog = null;
+var inputMethod = null;
 let _startDate;
 let _defaultCssStylesheet = null;
 let _cssStylesheet = null;
@@ -173,6 +175,9 @@ function _initializeUI() {
     if (LoginManager.canLock())
         screenShield = new ScreenShield.ScreenShield();
 
+    inputMethod = new InputMethod.InputMethod();
+    Clutter.get_default_backend().set_input_method(inputMethod);
+
     messageTray = new MessageTray.MessageTray();
     panel = new Panel.Panel();
     keyboard = new Keyboard.Keyboard();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]