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.
Contents
Concepts
Cookbook
- Observing The Mouse
- Observing The Keyboard
- Observing Key Sequences
- Observing Modifiers
- Using A Keyboard State Machine
- Mapping A Keyboard
Code
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 Lama 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 respectively: 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 Lama'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 Lama has a couple of nice examples of observing the keyboard rather than a particular DOM node.
The Lama's "toggle" feature keyboard-driven "toggle key" can by found via
tibet config
:
$ tibet config lama.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 Lama 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.lama.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.lama.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 Lama 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 Lama treats 'Shift-Enter' as "run this command" in the console by triggering a 'ConsoleInput' signal as shown here:
TP.lama.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 Lama 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 Lama which observes Shift-Shift
:
TP.lama.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.lama.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.lama.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
.