WebAPI/KeboardIME: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
Line 209: Line 209:
       inputContext.setComposition('☃');
       inputContext.setComposition('☃');
       // end the composition and commit the text.
       // end the composition and commit the text.
       inputContext.commitText('☃');
       inputContext.endComposition('☃');
       ['S', 'N', 'O', 'W'].forEach(function (k) {
       ['S', 'N', 'O', 'W'].forEach(function (k) {
         // For capital Latin letters, keyCode is same as the charCode.
         // For capital Latin letters, keyCode is same as the charCode.

Revision as of 10:29, 18 July 2013

Virtual Keyboard/IME API

Introduction

Virtual Keyboard/IME API aims to implement the system IME as a Web App. It will be used in Firefox OS and use cases could be found in the Firefox OS Keyboard UX document(WIP).

The API provides the communication channel between the IME App and the other App that receives user's inputs.

It is very different from the IME API from Google that aims to re-use the system's IME in a web page.

Status

API discussion:

  1. WebAPI mailing list post
  2. Extended API mailing list post

Implementation:

  1. bug 737110 - Bug 737110 - Virtual Keyboard API
  2. bug 805586 - [keyboard] keyboard needs a 'hide keyboard' button(main tracking bug)
  3. bug 844716 - Enable keyboard Apps to get/set selection range of the input field
  4. bug 860546 - [keyboard] JS changes to a textfield while keyboard is displayed do not get passed to keyboard
  5. bug 861665 - Allow IME to get notification when text field content is changed
  6. bug 861515 - Keyboard should be able to modify the text of the input field directly
  7. bug 838308 - mozKeyboard should require a permission to use
  8. bug 842436 - Keyboard API: There should only be one keyboard active, and Gecko should block interaction from non-active keyboards

Features

The Virtual Keyboard/IME API supports the following features:

  • Notifies the VKB app when the focus text field was changing in other

apps

  • Allow user to manual hide the keyboard. Check bug 737110.
  • The VKB app should be responsive to properties and the state of the input field (more than HTML5 input type, including current content, cursor position, x-inputmode bug 796544).
  • Sends trust
  • The VKB app should be able to send trusted key events such as they are considered by the other apps as user' inputs.
  • The VKB app should be able to send a character or a string to the current input cursor position.
  • Keyboard should be able to overwrite the current value of the input field of the input field and set the cursor position.
  • The VKB app should be able to move the cursor position or select a specified range of text.
  • The VKB should be able to switch the focus onto the previous/next input field.
  • The return key label of the VKB can be customized.

Proposed API

The input method API is available to web content who intend to implement an input method, or "input source", or "virtual keyboard".

partial interface Navigator {
    readonly attribute InputMethod inputMethod;
};
interface InputMethod: EventTarget {
    // Input Method Manager contain a few global methods expose to apps
    readonly attribute InputMethodManager mgmt;

    // Fired when the input context changes, include changes from and to null.
    // The new InputContext instance will be available in the event object under |inputcontext| property.
    // When it changes to null it means the app (the user of this API) no longer has the control of the original focused input field.
    // Note that if the app saves the original context, it might get void; implementation decides when to void the input context.
    attribute EventHandler oninputcontextchange;

    // An "input context" is mapped to a text field that the app is allow to mutate.
    // this attribute should be null when there is no text field currently focused.
    readonly attribute InputContext? inputcontext;
};
// Manages the list of IMEs, enables/disables IME and switches to an IME.
interface InputMethodManager {
    // Ask the OS to show a list of available IMEs for users to switch from.
    // OS should ignore this request if the app is currently not the active one.
    void showAll();

    // Ask the OS to switch away from the current active Keyboard app.
    // OS should ignore this request if the app is currently not the active one.
    void next();

    // Clear the focus of the current input field.
    // The OS might respond with hidden of the virtual keyboard and void the input context.
    void removeFocus();
 };
// The input context, which consists of attributes and information of current input field.
// It also hosts the methods available to the keyboard app to mutate the input field represented.
// An "input context" gets void when the app is no longer allowed to interact with the text field,
// e.g., the text field does no longer exist, the app is being switched to background, and etc.
interface InputContext {
   // The tag name of input field, which is enum of "input", "textarea", or "contenteditable"
   DOMString name;

   // The type of the input field, which is enum of text, number, password, url, search, email, and so on.
   // See http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#states-of-the-type-attribute
   DOMString type;
 
   /*
    * The inputmode string, representing the input mode.
    * See http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#input-modalities:-the-inputmode-attribute
    */
   DOMString inputmode;
 
   /*
    * The primary language for the input field.
    * It is the value of HTMLElement.lang.
    * See http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#htmlelement
    */
   DOMString lang;
 
   /*
    * Get the whole text content of the input field.
    * App is encouraged to use getSurroundingText() below to prevent getting to much things clogged in memory.
    */
   Promise<DOMString> getText();
  
    // The start and stop position of the selection.
    readonly attribute long selectionStart;
    readonly attribute long selectionEnd;

    /*
     * Set the selection range of the the editable text.
     * Note: This method cannot be used to move the cursor during composition. Calling this
     * method will cancel composition.
     * @param start The beginning of the selected text.
     * @param length The length of the selected text.
     *
     * Note that the start position should be less or equal to the end position.
     * To move the cursor, set the start and end position to the same value.
     */
    Promise<boolean> setSelectionRange(long start, long length);

    // User moves the cursor, changes the selection, or alters the composing text length
    // TODO: dup with the onsurroundingtextchange event/callback below for cursor moment?
    attribute EventHandler onselectionchange;

   /*
    * Get the text content around the cursor of the input field.
    * The text length is limited to 100 characters for each back and forth direction.
    * App is encouraged to use this instead of getText() above to prevent getting to much things clogged in memory.
    * TODO: how to return two values in one Promise object? as two arguments to the callback?
    */
    Promise<DOMString beforeText, DOMString afterText> getSurroundingText();
  
    /*
     *
     * Delete text around the cursor.
     * @param offset The offset from the cursor position where deletion starts.
     * @param length The length of text to delete.
     * TODO: maybe updateSurroundingText(DOMString beforeText, DOMString afterText); ?
     */
    Promise<boolean> deleteSurroundingText(long offset, long length);

    /*
    * This event is sent when the text around the cursor is changed, due to either text
    * editing or cursor movement. 
    *
    * The event handler function is specified as:
    * @param beforeString Text before and including cursor position.
    * @param afterString Text after and excluing cursor position.
    * function(DOMString beforeText, DOMString afterText) {
    * ...
    *  }
    * TODO: Is this a DOM Event or an attribute allows callback attachment? We should align the wording here.
    * TODO2: should this be |oncursorpositionchange|?
    */
    attribute SurroundingTextChangeEventHandler onsurroundingtextchange;
 
    /*
      * send a character with its key events.
      * TODO: what does Promise object returns?
      * TODO2: what are the modifiers?
      * Alternative: sendKey(KeyboardEvent event), but we will likely waste memory for creating the KeyboardEvent object.
      */    
    Promise<boolean> sendKey(long keyCode, long charCode, long modifiers);

    /*
     * Set current composition. It will start or update composition.
     * @param cursor Position in the text of the cursor.
     */
    Promise<boolean> setComposition(DOMString text, long cursor);

    /*
     * endComposition and actually commit the text.
     * Ending the composition with an empty string will not send any text.
     * (was |commitText(text, offset, length)|)
     *
     * @param text The text
     */
    Promise<boolean> endComposition(DOMString text);
};

Examples

The following "snowman filler" Keyboard app will start filling snowman character ("☃") and follow by characters "SNOW" with key events to the input field whenever the user is focus on a input field and switch to the keyboard app.

If the field is a numeric field, it will fill "1337".

var timer; 
function startTyping(inputContext) {
  clearTimeout(timer);
  timer = setInterval(function typing() {
    if (inputContext.inputmode === 'numeric' || inputContext.type === 'number') {
      ['1', '3', '3', '7'].forEach(function (k) {
        // For numbers, keyCode is same as the charCode.
        inputContext.sendKey(k.charCodeAt(0), k.charCodeAt(0));
      });
    } else {
      // It's not a good idea to commit text w/o sending events. So we should first send composition events.
      inputContext.setComposition('☃');
      // end the composition and commit the text.
      inputContext.endComposition('☃');
      ['S', 'N', 'O', 'W'].forEach(function (k) {
        // For capital Latin letters, keyCode is same as the charCode.
        inputContext.sendKey(k.charCodeAt(0), k.charCodeAt(0));
      });
  }, 1000);
}

function stopTyping() {
  clearTimeout(timer);
}

var im = navigator.inputMethod;

im.addEventListener('inputcontextchange', function contextchanged(evt) {
  if (evt.inputcontext) {
     // Got a new context, start working with it.
     startTyping(evt.inputcontext);
  } else {
     // The user have removed the focus, we are not allow to type into the text field anymore.
     stopTyping();
  }
});

if (im.inputcontext) {
  // The webpage here is loaded *after* the user has place the focus on the text field,
  // let's start typing now.
  startTyping(im.inputcontext);
}

Related

Android IME API:

http://developer.android.com/guide/topics/text/creating-input-method.html#IMEAPI

iOS Keyboard Management:

http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW1

Chrome Extensions API:

http://developer.chrome.com/trunk/extensions/input.ime.html