Implement automatically entering tab complete mode after a short delay
This seems to work. Manually tested for sanity.
This commit is contained in:
parent
460f68caef
commit
452c265e6a
2 changed files with 32 additions and 23 deletions
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
var Entry = require("./TabCompleteEntries").Entry;
|
var Entry = require("./TabCompleteEntries").Entry;
|
||||||
|
|
||||||
const DELAY_TIME_MS = 500;
|
const DELAY_TIME_MS = 1000;
|
||||||
const KEY_TAB = 9;
|
const KEY_TAB = 9;
|
||||||
const KEY_SHIFT = 16;
|
const KEY_SHIFT = 16;
|
||||||
const KEY_WINDOWS = 91;
|
const KEY_WINDOWS = 91;
|
||||||
|
@ -40,6 +40,7 @@ class TabComplete {
|
||||||
this.textArea = opts.textArea; // DOMElement
|
this.textArea = opts.textArea; // DOMElement
|
||||||
this.isFirstWord = false; // true if you tab-complete on the first word
|
this.isFirstWord = false; // true if you tab-complete on the first word
|
||||||
this.enterTabCompleteTimerId = null;
|
this.enterTabCompleteTimerId = null;
|
||||||
|
this.inPassiveMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,7 +69,8 @@ class TabComplete {
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
isTabCompleting() {
|
isTabCompleting() {
|
||||||
return this.completing;
|
// actually have things to tab over
|
||||||
|
return this.completing && this.matchedList.length > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
stopTabCompleting() {
|
stopTabCompleting() {
|
||||||
|
@ -130,36 +132,35 @@ class TabComplete {
|
||||||
* this event.
|
* this event.
|
||||||
*/
|
*/
|
||||||
onKeyDown(ev) {
|
onKeyDown(ev) {
|
||||||
|
var wasInPassiveMode = this.inPassiveMode && !ev.passive;
|
||||||
|
this.inPassiveMode = ev.passive;
|
||||||
|
|
||||||
if (ev.keyCode !== KEY_TAB) {
|
if (ev.keyCode !== KEY_TAB) {
|
||||||
if (this.completing && ev.keyCode !== KEY_SHIFT &&
|
if (this.completing && ev.keyCode !== KEY_SHIFT &&
|
||||||
!ev.metaKey && !ev.ctrlKey && !ev.altKey && ev.keyCode !== KEY_WINDOWS) {
|
!ev.metaKey && !ev.ctrlKey && !ev.altKey && ev.keyCode !== KEY_WINDOWS) {
|
||||||
// they're resuming typing; reset tab complete state vars.
|
// they're resuming typing; reset tab complete state vars.
|
||||||
this.stopTabCompleting();
|
this.stopTabCompleting();
|
||||||
|
|
||||||
|
// fall through to auto-enter-tab-completing if set
|
||||||
|
if (!this.opts.autoEnterTabComplete) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.opts.autoEnterTabComplete) {
|
if (this.opts.autoEnterTabComplete) {
|
||||||
/*
|
|
||||||
TODO:
|
|
||||||
- This is passive so we shouldn't clobber the partial word that
|
|
||||||
the user may have entered. This requires more logic to handle
|
|
||||||
that vs a normal TAB which does clobber.
|
|
||||||
- The first invocation of this timer will give no results because
|
|
||||||
we horribly set the enumeration onKeyDown in MessageComposer, which
|
|
||||||
was never actually hit. We should hook into RoomView's RoomState.members
|
|
||||||
event and set the list there.
|
|
||||||
|
|
||||||
|
|
||||||
clearTimeout(this.enterTabCompleteTimerId);
|
clearTimeout(this.enterTabCompleteTimerId);
|
||||||
this.enterTabCompleteTimerId = setTimeout(() => {
|
this.enterTabCompleteTimerId = setTimeout(() => {
|
||||||
if (!this.completing) {
|
if (!this.completing) {
|
||||||
// inject a fake tab event so we use the same code paths
|
// inject a fake tab event so we use the same code paths
|
||||||
|
// as much as possible. Add a 'passive' flag to indicate
|
||||||
|
// that they didn't really hit this key.
|
||||||
this.onKeyDown({
|
this.onKeyDown({
|
||||||
keyCode: KEY_TAB,
|
keyCode: KEY_TAB,
|
||||||
|
passive: true,
|
||||||
preventDefault: function(){} // NOP
|
preventDefault: function(){} // NOP
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, DELAY_TIME_MS); */
|
}, DELAY_TIME_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -179,8 +180,12 @@ class TabComplete {
|
||||||
this.nextMatchedEntry(-1);
|
this.nextMatchedEntry(-1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.nextMatchedEntry(1);
|
// if we were in passive mode we got out of sync by incrementing the
|
||||||
|
// index to show the peek view but not set the text area. Therefore,
|
||||||
|
// we want to set the *current* index rather than the *next* index.
|
||||||
|
this.nextMatchedEntry(wasInPassiveMode ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent the default TAB operation (typically focus shifting)
|
// prevent the default TAB operation (typically focus shifting)
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this._notifyStateChange();
|
this._notifyStateChange();
|
||||||
|
@ -204,13 +209,18 @@ class TabComplete {
|
||||||
else if (this.currentIndex < 0) {
|
else if (this.currentIndex < 0) {
|
||||||
this.currentIndex = this.matchedList.length - 1;
|
this.currentIndex = this.matchedList.length - 1;
|
||||||
}
|
}
|
||||||
var looped = this.currentIndex === 0; // catch forward and backward looping
|
var looped = (
|
||||||
|
// impossible to loop if they've never hit tab; catch forward and backward looping
|
||||||
|
!this.inPassiveMode && this.currentIndex === 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.inPassiveMode) {
|
||||||
// set textarea to this new value
|
// set textarea to this new value
|
||||||
this.textArea.value = this._replaceWith(
|
this.textArea.value = this._replaceWith(
|
||||||
this.matchedList[this.currentIndex].text,
|
this.matchedList[this.currentIndex].text,
|
||||||
this.currentIndex !== 0 // don't suffix the original text!
|
this.currentIndex !== 0 // don't suffix the original text!
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// visual display to the user that we looped - TODO: This should be configurable
|
// visual display to the user that we looped - TODO: This should be configurable
|
||||||
if (looped) {
|
if (looped) {
|
||||||
|
|
|
@ -340,7 +340,6 @@ module.exports = React.createClass({
|
||||||
if (!room || !this.tabComplete) {
|
if (!room || !this.tabComplete) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("_updateTabCompleteList");
|
|
||||||
this.tabComplete.setCompletionList(
|
this.tabComplete.setCompletionList(
|
||||||
MemberEntry.fromMemberList(room.getJoinedMembers())
|
MemberEntry.fromMemberList(room.getJoinedMembers())
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue