Devices

TIBET Devices

wins

  • Easily observe the Mouse, regardless of the specific target element.
  • Easily respond to specific Keyboard sequences regardless of focus.
  • Device support can be extended to other devices and integrated.

concepts

Browser-based development is heavily event-driven but it's also very "DOM centric" by default. For most applications tying your event handlers for mouse events to specific elements in the DOM is sufficient…but what about an IDE like the Sherpa where you want to watch the Mouse regardless of which element it might be over? Or, in a similar fashion, what if you want to know when someone pressed a particular key combination regardless of what element had focus?

To let you create handler logic at the "device level" rather than "DOM level" TIBET implements a top-level TP.core.Device type and two primary subtypes responsible for the mouse and keyboard repectively: TP.core.Mouse and TP.core.Keyboard.

Coupled with TIBET Signaling the device types allow you to observe and respond to activity from the mouse and keyboard without relying on a specific DOM element.

TP.core.Mouse

The TP.core.Mouse type provides support for a wide range of mouse events, all of which are ultimately normalized and observed/dispatched as TIBET signals.

Here's a rough list of the low-level events the mouse can process:

mousedown
mousemove
mouseup
mouseover
mouseout
mouseenter
mouseleave
click
dblclick
contextmenu
mousewheel
mousehover
dragdown
dragmove
dragup
dragover
dragout
draghover

A good use case for more generalized mouse-level observation is the Sherpa's "connector" functionality. When creating a visual connector between elements we can't know what specific items in the DOM we might want to observe…so we observe the TP.core.Mouse for both move (keep connecting) and up (stop connecting) triggers:

//  Observe the mouse directly for DOMDragMove and DOMDragUp signals, We'll
//  process those in our instance-level handlers above.
this.observe(TP.core.Mouse, TP.ac('TP.sig.DOMDragMove', 'TP.sig.DOMDragUp'));

You might also find TP.core.Mouse to be of use in other graphics-oriented applications, mapping applications, form-builders, etc.

TP.core.Keyboard

Like mouse events, key events are typically DOM-centric. You observe a specific input element for specific key codes, respond, and that's often sufficient.

What about when you want an application to treat a specific key, say F2, like a global help button? Or when you want any invocation of Esc to do something special?

As with the mouse, the TP.core.Keyboard supports a number of low-level events which ultimately are translated into the TIBET signals your code interact with:

keyup
keydown
keypress
modifierkeychange
altDown
ctrlDown
metaDown
shiftDown

TIBET signals for the keyboard are "synthetically named", in that they attempt to normalize naming conventions so you can observe precisely what you need to.

The TP.core.Device type does the translation in getDOMSignalName, which follows the logic from ~lib/lib/dat/TP.core.USAscii101Keyboard.xml:

TIBET signal names are computed from this map using the following algorithm:

1.  The name starts with the String 'DOM_'
2.  If any of the modifier keys (Meta, Ctrl, Alt or Shift) are pressed,
  that word is added to the name (i.e. 'Ctrl_'). There is an order by
  which multiple modifiers can be specified:

    a.  'Meta'
    b.  'Ctrl'
    c.  'Alt'
    d.  'Shift'

  So a signal name could be: 'DOM_Ctrl_Shift_Tab_Down', but *not*
  'DOM_Shift_Ctrl_Tab_Down'

3.  If the key has a value for the 'key=' attribute in this map, that
  value is used. NOTE 'Shift' must be present for uppercase:

    DOM_Ctrl_Percent_Up
    DOM_Ctrl_A_Up           //  NB: Lowercase 'a'
    DOM_Ctrl_Shift_A_Up     //  NB: Uppercase 'A'

4.  If the key does not have a value for the 'key=' attribute in this
  map, the value in the 'glyph=' attribute is used:

    DOM_1_Up

5.  In addition to either the 'key' or 'glyph' value being used in a
  signal name, TIBET will also generate a second signal name using the
  Unicode value stored in the 'char=' attribute. This allows
  difficult-to-encode keys to be observed:

    DOM_U0027_Up            //  NB: Same as 'DOM_Right_Up'

As with the mouse the Sherpa has a couple of nice examples of observing the keyboard rather than a particular DOM node.

The Sherpa's "toggle" feature keyboard-driven "toggle key" can by found via tibet config:

$ tibet config sherpa.toggle_key
TP.sig.DOM_Alt_Up_Up

NOTE the Alt_Up_Up portion. That's the Alt key modifying the Up key (aka the upward arrow) and the Up (rather than down or press) operation. So effectively it says "Alt-UpArrow" "up" is the toggle.

The Sherpa then observes the TP.core.Keyboard for that sequence without concern for any particular DOM element:

    ...
    }).observe(TP.core.Keyboard, toggleKey);

Using the TP.core.Device types you can observe events without a DOM. You can also trigger them for testing and other operations, again without relying on a particular DOM configuration.

cookbook

Observing The Mouse

Observing the mouse is simple via TIBET's observe functionality. You can invoke observe via a number of pathways (see TIBET Signaling for details).

The sample below is from TP.sherpa.connector and uses an object method which ensures that the dispatch will look for a handler method on the connector instance:

this.observe(TP.core.Mouse, TP.ac('TP.sig.DOMDragMove', 'TP.sig.DOMDragUp'));

...

TP.sherpa.connector.Inst.defineHandler('DOMDragMove',
function(aSignal) {
...
});

Invoke this.ignore or suitable ignore operations to turn off your observations.

Observing The Keyboard

Like the mouse, you can observe the keyboard using any of TIBET's observe pathways. The one caveat for working with the keyboard is that you should observe the "current keyboard" instance (which is created and initialized with the tibet.keyboard map necessary to process key bindings.

In the example below we see code from the Sherpa which sets up a handler for an 'up' operation from the Enter key (which virtually all JS on the internet observes by hard-coding for keycode 13) augmented with the Shift modifier.

The Sherpa treats 'Shift-Enter' as "run this command" in the console by triggering a 'ConsoleInput' signal as shown here:

TP.sherpa.NormalKeyResponder.Inst.defineHandler('DOM_Shift_Enter_Up',
function(aSignal) {

    /**
     * @method handleDOM_Shift_Enter_Up
     * @summary Executes the current console input.
     * @param {TP.sig.StateInput} aSignal The signal that caused the state
     *     machine to get further input. The original triggering signal (most
     *     likely a keyboard-related signal) will be in this signal's payload
     *     under the key 'trigger'.
     * @returns {TP.core.NormalKeyResponder} The receiver.
     */

    this.get('$consoleService')[TP.composeHandlerName('ConsoleInput')](aSignal);

    return this;
});

Observing Keyboard Sequences

In TIBET you can observe sequences of keys to trigger behavior. For example, in the Sherpa hitting the Shift key twice in succession will cause the input focus to move to the TIBET Developer Console input cell.

To observe a sequence use defineHandler like you would for a regular signal but separate the keys with __ (double underscore).

Here's code from the ConsoleService of the Sherpa which observes Shift-Shift:

TP.sherpa.NormalKeyResponder.Inst.defineHandler('DOM_Shift_Up__DOM_Shift_Up',
function(aSignal) {

    /**
     * @method handleDOM_Shift_Up__DOM_Shift_Up
     * @summary Focuses the input cell.
     * @param {TP.sig.StateInput} aSignal The signal that caused the state
     *     machine to get further input. The original triggering signal (most
     *     likely a keyboard-related signal) will be in this signal's payload
     *     under the key 'trigger'.
     * @returns {TP.core.NormalKeyResponder} The receiver.
     */

    var consoleGUI;

    consoleGUI = this.get('$consoleGUI');

    //  Focus the console GUI's input and set its cursor to the end.
    consoleGUI.focusInput();
    consoleGUI.setInputCursorToEnd();

    aSignal.stopPropagation();

    return this;
});

TIBET has a very efficient keyboard sequence testing algorithm to make sure the sequence happens as you intend and that intermediate keys will reset the pattern.

Observing Modifier Keys

If your application depends on which modifier keys (Shift, Ctrl, Alt, Meta) might be down at a particular time (or which ones might transition to up during an operation) you can observe that behavior by observing TIBET's DOMModifierKeyChange signal:

this.observe(TP.core.Keyboard.getCurrentKeyboard(), 'TP.sig.DOMModifierKeyChange');

...

TP.sherpa.ConsoleService.Inst.defineHandler('DOMModifierKeyChange',
function(aSignal) {
...
});

Invoke this.ignore or suitable ignore operations to turn off your observations.

Using A Keyboard State Machine

TIBET's support for state machines allows you to perform very complex keyboard handing if necessary. We'll be working to expand documenation for this process in the future but for now you can refer to the TP.sherpa.ConsoleService and its configureKeyboardStateMachine instance method definition for sample code.

Mapping A Keyboard

Mapping a keyboard is a complex operation but something TIBET does make possible.

The default keyboard in TIBET is managed by the TP.core.USAscii101Keyboard type, a subtype of TP.core.Keyboard. To create your own keyboard mapping you'll need to create a similar subtype and adjust its implementation as needed.

See ~lib/src/tibet/kernel/TIBETDeviceTypes.js for code samples specific to both the root keyboard and ASCII keyboard types.

In addition to creating a type to manage your keyboard you'll need to create a key mapping file which defines the keycodes and their mappings. The default system map for ASCII is found in ~lib/lib/dat/TP.core.USAscii101Keyboard.xml and serves as a good model for this.

Once you have a suitable keyboard map you can define it as the default one using TIBET flags, in particular tibet.keyboard should be set to the name of your keyboard type.

Finally, you'll need to make sure your type and mapping file are loaded by your application. See the documentation on the TIBET Loader for more on this subject.

code

Primary code for the TP.core.Mouse and TP.core.Keyboard can be found in the TIBET kernel in ~lib/src/tibet/kernel/TIBETDeviceTypes.js.

The default keyboard map is found in ~lib/lib/dat/TP.core.USAscii101Keyboard.xml.