Finalize current keymapping work:

- catchup with latest changes to pilot/keys.js
- the textinput adds a event.addCommandKeyEvent(...) to the input
- the onTextInput and onCommandKey event sent by the textinput are forwareded to the edtior which
  then forwards the input to the keybinding, which handles the input based on the current inputMode.
- the current inputMode can get changed by setting the setting "inputMode". Currently there are the
  modes "vim" and "emacs". If the inputMode doesn't match, the "default" mode is used.
This commit is contained in:
Julian Viereck 2010-12-31 18:32:53 +01:00
commit 3ebd2865ea
6 changed files with 95 additions and 94 deletions

View file

@ -253,8 +253,8 @@ canon.addCommand({
canon.addCommand({ canon.addCommand({
name: "inserttext", name: "inserttext",
exec: function(env, args, request) { exec: function(env, args, request) {
env.editor.onTextInput(lang.stringRepeat(args.text || "", env.editor.insert(lang.stringRepeat(args.text || "",
args.times || 1)); args.times || 1));
} }
}); });

View file

@ -54,7 +54,7 @@ var Editor =function(renderer, doc) {
this.renderer = renderer; this.renderer = renderer;
this.textInput = new TextInput(container, this); this.textInput = new TextInput(container, this);
this.keyBinding = new KeyBinding(container, this); this.keyBinding = new KeyBinding(this);
var self = this; var self = this;
event.addListener(container, "mousedown", function(e) { event.addListener(container, "mousedown", function(e) {
setTimeout(function() {self.focus();}); setTimeout(function() {self.focus();});
@ -64,7 +64,7 @@ var Editor =function(renderer, doc) {
return event.preventDefault(e); return event.preventDefault(e);
}); });
var mouseTarget = renderer.getMouseEventTarget(); var mouseTarget = renderer.getMouseEventTarget();
event.addListener(mouseTarget, "mousedown", this.onMouseDown.bind(this)); event.addListener(mouseTarget, "mousedown", this.onMouseDown.bind(this));
event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseDoubleClick.bind(this)); event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseDoubleClick.bind(this));
event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseTripleClick.bind(this)); event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseTripleClick.bind(this));
@ -403,7 +403,7 @@ var Editor =function(renderer, doc) {
} }
}; };
this.onTextInput = function(text) { this.insert = function(text) {
if (this.$readOnly) if (this.$readOnly)
return; return;
@ -480,6 +480,14 @@ var Editor =function(renderer, doc) {
_self.renderer.scrollCursorIntoView(); _self.renderer.scrollCursorIntoView();
}); });
}); });
}
this.onTextInput = function(text) {
this.keyBinding.onTextInput(text);
};
this.onCommandKey = function(e, hashId, keyCode) {
this.keyBinding.onCommandKey(e, hashId, keyCode);
}; };
this.$overwrite = false; this.$overwrite = false;
@ -592,7 +600,7 @@ var Editor =function(renderer, doc) {
if (this.selection.isEmpty()) if (this.selection.isEmpty())
this.selection.selectLeft(); this.selection.selectLeft();
this.moveCursorToPosition(this.doc.remove(this.getSelectionRange())); this.moveCursorToPosition(this.doc.remove(this.getSelectionRange()));
this.clearSelection(); this.clearSelection();
}; };
@ -892,7 +900,7 @@ var Editor =function(renderer, doc) {
else { else {
times = times || 1; times = times || 1;
while (times--) { while (times--) {
this.selection.moveCursorLeft(); this.selection.moveCursorLeft();
} }
} }
this.clearSelection(); this.clearSelection();

View file

@ -39,102 +39,45 @@
define(function(require, exports, module) { define(function(require, exports, module) {
var useragent = require("pilot/useragent"); var useragent = require("pilot/useragent");
var keyUtil = require("pilot/keys");
var event = require("pilot/event"); var event = require("pilot/event");
var settings = require("pilot/settings").settings;
var default_mac = require("ace/conf/keybindings/default_mac").bindings; var default_mac = require("ace/conf/keybindings/default_mac").bindings;
var default_win = require("ace/conf/keybindings/default_win").bindings; var default_win = require("ace/conf/keybindings/default_win").bindings;
var canon = require("pilot/canon"); var canon = require("pilot/canon");
require("ace/commands/default_commands"); require("ace/commands/default_commands");
var vim_mode = require("ace/mode/vim"); var inputModes = {
var emacs_mode = require("ace/mode/emacs"); vim: require("ace/mode/vim"),
emacs: require("ace/mode/emacs")
};
var KeyBinding = function(element, editor, config) { var KeyBinding = function(editor, config) {
this.$editor = editor;
this.$data = { };
this.setConfig(config); this.setConfig(config);
var data = { };
var _self = this; // PROBLEM: When this file is loaded via require.js, the types are already
event.addKeyListener(element, function(e) { // declaired, BUT the startup() function is not called in pilot/types/basic.
// opera on mac swaps ctrl and meta keys // Therefore the settings are not declaired. As a workaround, the setting
if (useragent.isOpera && useragent.isMac) // is declaired when the KeyBinding is created.
var hashId = 0 | (e.metaKey ? 1 : 0) | (e.altKey ? 2 : 0) var InputModeSetting = {
| (e.shiftKey ? 4 : 0) | (e.ctrlKey ? 8 : 0); name: "inputMode",
else description: "Which input mode do you want to use?",
var hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) type: "text",
| (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0); defaultValue: "standard"
};
console.log(hashId, e.keyCode); this.$inputMode = null;
var key; settings.addSetting(InputModeSetting);
var keyCode = e.keyCode;
// TODO: Pressing the command key on Chrome/Mac causes the keyCode settings.getSetting("inputMode").addEventListener("change", function(e) {
// to be 91, which is the "[" character, without pressing this actually. this.$inputMode = inputModes[e.value];
// Invastigate more further and make a proper work around. this.$data = { };
if (hashId & 8 && keyCode == 91) { }.bind(this));
key = "";
} else {
key = (_self.keyNames[e.keyCode] ||
String.fromCharCode(e.keyCode)).toLowerCase();
}
var toExecute;
if (true) {
var toExecute =
// vim_mode.handleKeyboard(data, hashId, key, e);
emacs_mode.handleKeyboard(data, hashId, key, e);
}
// If there is nothing to execute yet, then use the default keymapping.
if (!toExecute) {
toExecute = {
command: (_self.config.reverse[hashId] || {})[key]
};
}
// If there is something to execute, then go for it.
if (toExecute) {
var success = canon.exec(toExecute.command,
{editor: editor}, toExecute.args);
if (success) {
return event.stopEvent(e);
}
}
});
}; };
(function() { (function() {
this.keyMods = {"ctrl": 1, "alt": 2, "option" : 2, "shift": 4, "meta": 8, "command": 8};
this.keyNames = {
"8" : "Backspace",
"9" : "Tab",
"13" : "Enter",
"27" : "Esc",
"32" : "Space",
"33" : "PageUp",
"34" : "PageDown",
"35" : "End",
"36" : "Home",
"37" : "Left",
"38" : "Up",
"39" : "Right",
"40" : "Down",
"45" : "Insert",
"46" : "Delete",
"107": "+",
"112": "F1",
"113": "F2",
"114": "F3",
"115": "F4",
"116": "F5",
"117": "F6",
"118": "F7",
"119": "F8",
"120": "F9",
"121": "F10",
"122": "F11",
"123": "F12"
};
function splitSafe(s, separator, limit, bLowerCase) { function splitSafe(s, separator, limit, bLowerCase) {
return (bLowerCase && s.toLowerCase() || s) return (bLowerCase && s.toLowerCase() || s)
.replace(/(?:^\s+|\n|\s+$)/g, "") .replace(/(?:^\s+|\n|\s+$)/g, "")
@ -149,8 +92,8 @@ var KeyBinding = function(element, editor, config) {
l = parts.length; l = parts.length;
for (; i < l; ++i) { for (; i < l; ++i) {
if (this.keyMods[parts[i]]) if (keyUtil.KEY_MODS[parts[i]])
hashId = hashId | this.keyMods[parts[i]]; hashId = hashId | keyUtil.KEY_MODS[parts[i]];
else else
key = parts[i] || "-"; //when empty, the splitSafe removed a '-' key = parts[i] || "-"; //when empty, the splitSafe removed a '-'
} }
@ -184,6 +127,47 @@ var KeyBinding = function(element, editor, config) {
this.config.reverse = objectReverse.call(this, this.config, "|"); this.config.reverse = objectReverse.call(this, this.config, "|");
}; };
this.onCommandKey = function(e, hashId, keyCode) {
key = (keyUtil[keyCode] ||
String.fromCharCode(keyCode)).toLowerCase();
var toExecute;
if (this.$inputMode) {
toExecute =
this.$inputMode.handleKeyboard(this.$data, hashId, key, e);
}
// If there is nothing to execute yet, then use the default keymapping.
if (!toExecute) {
toExecute = {
command: (this.config.reverse[hashId] || {})[key]
};
}
// If there is something to execute, then go for it.
if (toExecute) {
var success = canon.exec(toExecute.command,
{editor: this.$editor}, toExecute.args);
if (success) {
return event.stopEvent(e);
}
}
};
this.onTextInput = function(text) {
if (this.$inputMode) {
var toExecute =
this.$inputMode.handleKeyboard(this.$data, 0, text, {});
var success = canon.exec(toExecute.command,
{editor: this.$editor}, toExecute.args);
if (success) {
return;
}
}
this.$editor.insert(text);
}
}).call(KeyBinding.prototype); }).call(KeyBinding.prototype);
exports.KeyBinding = KeyBinding; exports.KeyBinding = KeyBinding;

View file

@ -37,6 +37,10 @@
define(function(require, exports, module) { define(function(require, exports, module) {
// If you're developing a new keymapping and want to get an idea what's going
// on, then enable debugging.
var DEBUG = false;
function KeyboardStateMapper(keymapping) { function KeyboardStateMapper(keymapping) {
this.keymapping = this.$buildKeymappingRegex(keymapping); this.keymapping = this.$buildKeymappingRegex(keymapping);
} }
@ -172,7 +176,9 @@ KeyboardStateMapper.prototype = {
result.command = "null"; result.command = "null";
} }
console.log("KeyboardStateMapper#find", binding); if (DEBUG) {
console.log("KeyboardStateMapper#find", binding);
}
return true; return true;
}); });
@ -191,7 +197,9 @@ KeyboardStateMapper.prototype = {
var symbolicName = r.symbolicName; var symbolicName = r.symbolicName;
r = this.$find(data, buffer, symbolicName, hashId, key); r = this.$find(data, buffer, symbolicName, hashId, key);
console.log("KeyboardStateMapper#match", buffer, symbolicName, r); if (DEBUG) {
console.log("KeyboardStateMapper#match", buffer, symbolicName, r);
}
return r; return r;
} }

View file

@ -114,6 +114,7 @@ var TextInput = function(parentNode, host) {
setTimeout(sendText, 0); setTimeout(sendText, 0);
}; };
event.addCommandKeyListener(text, host.onCommandKey.bind(host));
event.addListener(text, "keypress", onTextInput); event.addListener(text, "keypress", onTextInput);
event.addListener(text, "textInput", onTextInput); event.addListener(text, "textInput", onTextInput);
event.addListener(text, "paste", onTextInput); event.addListener(text, "paste", onTextInput);

@ -1 +1 @@
Subproject commit 36c55c39e8ede94e964364e01ab5308f3f51c2eb Subproject commit cd074fd37a02bc40300aa7b120d395525767b156