mirror of
https://github.com/vale981/tridactyl
synced 2025-03-04 09:01:39 -05:00
Merge pull request #4809 from petoncle/attempt-to-buffer-keyboard-events-until-commandline-opened
Reduce likelihood of losing characters while command line is opening
This commit is contained in:
commit
ec342c4839
5 changed files with 104 additions and 27 deletions
|
@ -80,6 +80,7 @@ const commandline_state = {
|
|||
*/
|
||||
isVisible: false,
|
||||
keyEvents: new Array<MinimalKey>(),
|
||||
initialClInputValue: "",
|
||||
refresh_completions,
|
||||
state,
|
||||
}
|
||||
|
@ -91,7 +92,6 @@ theme(document.querySelector(":root"))
|
|||
function resizeArea() {
|
||||
if (commandline_state.isVisible) {
|
||||
Messaging.messageOwnTab("commandline_content", "show")
|
||||
Messaging.messageOwnTab("commandline_content", "focus")
|
||||
focus()
|
||||
}
|
||||
}
|
||||
|
@ -162,9 +162,31 @@ const noblur = () => setTimeout(() => commandline_state.clInput.focus(), 0)
|
|||
|
||||
/** @hidden **/
|
||||
export function focus() {
|
||||
function consumeBufferedPageKeys(bufferedPageKeys: string[]) {
|
||||
const clInputStillFocused = window.document.activeElement === commandline_state.clInput;
|
||||
logger.debug("stop_buffering_page_keys response received, bufferedPageKeys = ", bufferedPageKeys,
|
||||
"clInputStillFocused = " + clInputStillFocused)
|
||||
if (bufferedPageKeys.length !== 0) {
|
||||
const currentClInputValue = commandline_state.clInput.value;
|
||||
const initialClInputValue = commandline_state.initialClInputValue;
|
||||
logger.debug("Consuming buffered page keys", bufferedPageKeys,
|
||||
"initialClInputValue = " + initialClInputValue,
|
||||
"currentClInputValue = " + currentClInputValue);
|
||||
// Native events are assumed to be character keydown events,
|
||||
// i.e. characters appended at the end of clInput.
|
||||
commandline_state.clInput.value =
|
||||
initialClInputValue
|
||||
+ bufferedPageKeys.join("")
|
||||
+ currentClInputValue.substring(initialClInputValue.length)
|
||||
// Update completion.
|
||||
clInputValueChanged()
|
||||
}
|
||||
}
|
||||
commandline_state.clInput.focus()
|
||||
commandline_state.clInput.removeEventListener("blur", noblur)
|
||||
commandline_state.clInput.addEventListener("blur", noblur)
|
||||
logger.debug("commandline_frame clInput focus(), activeElement is clInput: " + (window.document.activeElement === commandline_state.clInput))
|
||||
Messaging.messageOwnTab("stop_buffering_page_keys").then(consumeBufferedPageKeys)
|
||||
}
|
||||
|
||||
/** @hidden **/
|
||||
|
@ -188,6 +210,7 @@ commandline_state.clInput.addEventListener(
|
|||
"keydown",
|
||||
function (keyevent: KeyboardEvent) {
|
||||
if (!keyevent.isTrusted) return
|
||||
logger.debug("commandline_frame clInput keydown event listener", keyevent)
|
||||
commandline_state.keyEvents.push(minimalKeyFromKeyboardEvent(keyevent))
|
||||
const response = keyParser(commandline_state.keyEvents)
|
||||
if (response.isMatch) {
|
||||
|
@ -264,6 +287,12 @@ export function refresh_completions(exstr) {
|
|||
let onInputPromise: Promise<any> = Promise.resolve()
|
||||
/** @hidden **/
|
||||
commandline_state.clInput.addEventListener("input", () => {
|
||||
logger.debug("commandline_frame clInput input event listener")
|
||||
clInputValueChanged();
|
||||
})
|
||||
|
||||
/** @hidden **/
|
||||
function clInputValueChanged() {
|
||||
const exstr = commandline_state.clInput.value
|
||||
contentState.current_cmdline = exstr
|
||||
contentState.cmdline_filter = ""
|
||||
|
@ -282,7 +311,7 @@ commandline_state.clInput.addEventListener("input", () => {
|
|||
contentState.cmdline_filter = exstr
|
||||
})
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
|
||||
/** @hidden **/
|
||||
let cmdline_history_current = ""
|
||||
|
@ -341,8 +370,10 @@ export function fillcmdline(
|
|||
trailspace = true,
|
||||
ffocus = true,
|
||||
) {
|
||||
logger.debug("commandline_frame fillcmdline(newcommand = " + newcommand + " trailspace = " + trailspace + " ffocus = " + ffocus + ")")
|
||||
if (trailspace) commandline_state.clInput.value = newcommand + " "
|
||||
else commandline_state.clInput.value = newcommand
|
||||
commandline_state.initialClInputValue = commandline_state.clInput.value
|
||||
commandline_state.isVisible = true
|
||||
let result = Promise.resolve([])
|
||||
// Focus is lost for some reason.
|
||||
|
@ -373,6 +404,7 @@ export function editor_function(fn_name: keyof typeof tri_editor, ...args) {
|
|||
}
|
||||
|
||||
Messaging.addListener("commandline_frame", Messaging.attributeCaller(SELF))
|
||||
logger.debug("Added commandline_frame message listener")
|
||||
|
||||
commandline_state.fns = getCommandlineFns(commandline_state)
|
||||
Messaging.addListener(
|
||||
|
@ -388,3 +420,5 @@ Messaging.addListener(
|
|||
;(window as any).tri = Object.assign(window.tri || {}, {
|
||||
perfObserver: perf.listenForCounters(),
|
||||
})
|
||||
|
||||
Messaging.messageOwnTab("commandline_frame_ready_to_receive_messages")
|
||||
|
|
|
@ -63,7 +63,7 @@ export function show(hidehover = false) {
|
|||
*
|
||||
* Inspired by VVimpulation: https://github.com/amedama41/vvimpulation/commit/53065d015d1e9a892496619b51be83771f57b3d5
|
||||
*/
|
||||
|
||||
logger.debug("commandline_content show()")
|
||||
if (hidehover) {
|
||||
const a = window.document.createElement("A")
|
||||
;(a as any).href = ""
|
||||
|
@ -96,18 +96,6 @@ export function hide() {
|
|||
}
|
||||
}
|
||||
|
||||
export function focus() {
|
||||
try {
|
||||
cmdline_iframe.focus()
|
||||
} catch (e) {
|
||||
// Note: We can't use cmdline_logger.error because it will try to log
|
||||
// the error in the commandline, which will need to focus() it again,
|
||||
// which will throw again...
|
||||
// cmdline_logger.error(e)
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
export function blur() {
|
||||
try {
|
||||
cmdline_iframe.blur()
|
||||
|
|
|
@ -14,6 +14,7 @@ import * as hinting from "@src/content/hinting"
|
|||
import * as gobblemode from "@src/parsers/gobblemode"
|
||||
import * as generic from "@src/parsers/genericmode"
|
||||
import * as nmode from "@src/parsers/nmode"
|
||||
import * as Messaging from "@src/lib/messaging";
|
||||
|
||||
const logger = new Logger("controller")
|
||||
|
||||
|
@ -64,6 +65,8 @@ class KeyCanceller {
|
|||
}
|
||||
|
||||
push(ke: KeyboardEvent) {
|
||||
ke.preventDefault()
|
||||
ke.stopImmediatePropagation()
|
||||
this.keyPress.push(ke)
|
||||
this.keyUp.push(ke)
|
||||
}
|
||||
|
@ -99,6 +102,26 @@ class KeyCanceller {
|
|||
|
||||
export const canceller = new KeyCanceller()
|
||||
|
||||
let commandlineFrameReadyToReceiveMessages = false
|
||||
Messaging.addListener("commandline_frame_ready_to_receive_messages", () => {
|
||||
logger.debug("Received commandline_frame_ready_to_receive_messages")
|
||||
commandlineFrameReadyToReceiveMessages = true
|
||||
})
|
||||
|
||||
let mustBufferPageKeysForClInput = false
|
||||
let bufferedPageKeys: string[] = []
|
||||
let bufferingPageKeysBeginTime: number
|
||||
Messaging.addListener("stop_buffering_page_keys", (message, sender, sendResponse) => {
|
||||
const bufferingDuration = performance.now() - bufferingPageKeysBeginTime;
|
||||
logger.debug("stop_buffering_page_keys request received, responding with bufferedPageKeys = ", bufferedPageKeys
|
||||
+ " bufferingDuration = " + bufferingDuration + "ms")
|
||||
sendResponse(Promise.resolve(bufferedPageKeys))
|
||||
// At this point, clInput is focused and the page cannot get any more keyboard events
|
||||
// until it is refocused.
|
||||
mustBufferPageKeysForClInput = false
|
||||
bufferedPageKeys = []
|
||||
})
|
||||
|
||||
/** Accepts keyevents, resolves them to maps, maps to exstrs, executes exstrs */
|
||||
function* ParserController() {
|
||||
const parsers: {
|
||||
|
@ -184,13 +207,17 @@ function* ParserController() {
|
|||
)
|
||||
|
||||
if (response.isMatch && keyevent instanceof KeyboardEvent) {
|
||||
keyevent.preventDefault()
|
||||
keyevent.stopImmediatePropagation()
|
||||
canceller.push(keyevent)
|
||||
}
|
||||
|
||||
if (response.exstr) {
|
||||
exstr = response.exstr
|
||||
if (exstr === "fillcmdline" || exstr === "fillcmdline_notrail") {
|
||||
logger.debug("Starting buffering of page keys")
|
||||
bufferingPageKeysBeginTime = performance.now()
|
||||
mustBufferPageKeysForClInput = true
|
||||
bufferedPageKeys = []
|
||||
}
|
||||
break
|
||||
} else {
|
||||
keyEvents = response.keys
|
||||
|
@ -215,7 +242,32 @@ function* ParserController() {
|
|||
export const generator = ParserController() // var rather than let stops weirdness in repl.
|
||||
generator.next()
|
||||
|
||||
/** Feed keys to the ParserController */
|
||||
/** Feed keys to the ParserController, unless they should be buffered to be later fed to clInput */
|
||||
export function acceptKey(keyevent: KeyboardEvent) {
|
||||
return generator.next(keyevent)
|
||||
function tryBufferingPageKeyForClInput(keyevent: KeyboardEvent) {
|
||||
if (!mustBufferPageKeysForClInput)
|
||||
return false;
|
||||
const bufferingDuration = performance.now() - bufferingPageKeysBeginTime;
|
||||
logger.debug("controller_content mustBufferPageKeysForClInput = " + mustBufferPageKeysForClInput
|
||||
+ " bufferingDuration = " + bufferingDuration + "ms");
|
||||
const isCharacterKey = keyevent.key.length == 1
|
||||
&& !keyevent.metaKey && !keyevent.ctrlKey && !keyevent.altKey && !keyevent.metaKey;
|
||||
if (isCharacterKey) {
|
||||
bufferedPageKeys.push(keyevent.key);
|
||||
logger.debug("Buffering page keys", bufferedPageKeys)
|
||||
}
|
||||
canceller.push(keyevent)
|
||||
return true
|
||||
}
|
||||
if (!commandlineFrameReadyToReceiveMessages) {
|
||||
// If the commandline frame cannot receive messages, the fillcmdline message sent by excmds.fillcmdline() to the
|
||||
// commandline frame will never be received. As a result, commandline_frame.focus() will not be called, which
|
||||
// in turn means that the stop_buffering_page_keys message will never be sent to the content/page process.
|
||||
// If the content/page process starts buffering keys for clInput, but the stop_buffering_page_keys message is never received,
|
||||
// it will keep buffering (and eating events) forever.
|
||||
logger.debug("controller_content Ignoring key event ", keyevent, " since commandline frame is not yet ready to receive messages", keyevent)
|
||||
return
|
||||
}
|
||||
if (!tryBufferingPageKeyForClInput(keyevent))
|
||||
return generator.next(keyevent)
|
||||
}
|
||||
|
|
|
@ -3892,11 +3892,11 @@ export function sleep(time_ms: number) {
|
|||
/** @hidden */
|
||||
//#content
|
||||
export function showcmdline(focus = true) {
|
||||
logger.debug("excmds showcmdline()")
|
||||
const hidehover = true
|
||||
CommandLineContent.show(hidehover)
|
||||
let done = Promise.resolve()
|
||||
if (focus) {
|
||||
CommandLineContent.focus()
|
||||
done = Messaging.messageOwnTab("commandline_frame", "focus")
|
||||
}
|
||||
return done
|
||||
|
@ -3912,16 +3912,17 @@ export function hidecmdline() {
|
|||
//#content
|
||||
export function fillcmdline(...strarr: string[]) {
|
||||
const str = strarr.join(" ")
|
||||
showcmdline()
|
||||
return Messaging.messageOwnTab("commandline_frame", "fillcmdline", [str])
|
||||
showcmdline(false)
|
||||
logger.debug("excmds fillcmdline sending fillcmdline to commandline_frame")
|
||||
return Messaging.messageOwnTab("commandline_frame", "fillcmdline", [str, true/*trailspace*/, true/*focus*/])
|
||||
}
|
||||
|
||||
/** Set the current value of the commandline to string *without* a trailing space */
|
||||
//#content
|
||||
export function fillcmdline_notrail(...strarr: string[]) {
|
||||
const str = strarr.join(" ")
|
||||
showcmdline()
|
||||
return Messaging.messageOwnTab("commandline_frame", "fillcmdline", [str, false])
|
||||
showcmdline(false)
|
||||
return Messaging.messageOwnTab("commandline_frame", "fillcmdline", [str, false/*trailspace*/, true/*focus*/])
|
||||
}
|
||||
|
||||
/** Show and fill the command line without focusing it */
|
||||
|
|
|
@ -15,6 +15,8 @@ export type TabMessageType =
|
|||
| "lock"
|
||||
| "alive"
|
||||
| "tab_changes"
|
||||
| "stop_buffering_page_keys"
|
||||
| "commandline_frame_ready_to_receive_messages"
|
||||
|
||||
export type NonTabMessageType =
|
||||
| "owntab_background"
|
||||
|
@ -132,7 +134,7 @@ export async function message<
|
|||
/** Message the active tab of the currentWindow */
|
||||
export async function messageActiveTab(
|
||||
type: TabMessageType,
|
||||
command: string,
|
||||
command?: string,
|
||||
args?: any[],
|
||||
) {
|
||||
return messageTab(await activeTabId(), type, command, args)
|
||||
|
@ -153,7 +155,7 @@ export async function messageTab(
|
|||
}
|
||||
|
||||
let _ownTabId
|
||||
export async function messageOwnTab(type: TabMessageType, command, args?) {
|
||||
export async function messageOwnTab(type: TabMessageType, command?, args?) {
|
||||
if (_ownTabId === undefined) {
|
||||
_ownTabId = await ownTabId()
|
||||
}
|
||||
|
@ -164,7 +166,7 @@ export async function messageOwnTab(type: TabMessageType, command, args?) {
|
|||
|
||||
export async function messageAllTabs(
|
||||
type: TabMessageType,
|
||||
command: string,
|
||||
command?: string,
|
||||
args?: any[],
|
||||
) {
|
||||
const responses = []
|
||||
|
|
Loading…
Add table
Reference in a new issue