Add a temporary solution to non-Latin keyboards being unusable

This commit is contained in:
Keavon Chambers 2021-12-23 13:29:02 -08:00
parent 720a04b501
commit 05737202fa
1 changed files with 342 additions and 10 deletions

View File

@ -37,37 +37,41 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
// Don't redirect when a modal is covering the workspace
if (dialog.dialogIsVisible()) return false;
const key = getLatinKey(e);
// Don't redirect a fullscreen request
if (e.key.toLowerCase() === "f11" && e.type === "keydown" && !e.repeat) {
if (key === "f11" && e.type === "keydown" && !e.repeat) {
e.preventDefault();
fullscreen.toggleFullscreen();
return false;
}
// Don't redirect a reload request
if (e.key.toLowerCase() === "f5") return false;
if (key === "f5") return false;
// Don't redirect debugging tools
if (e.key.toLowerCase() === "f12") return false;
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "c") return false;
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "i") return false;
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "j") return false;
if (key === "f12") return false;
if (e.ctrlKey && e.shiftKey && key === "c") return false;
if (e.ctrlKey && e.shiftKey && key === "i") return false;
if (e.ctrlKey && e.shiftKey && key === "j") return false;
// Redirect to the backend
return true;
};
const onKeyDown = (e: KeyboardEvent) => {
const key = getLatinKey(e);
if (shouldRedirectKeyboardEventToBackend(e)) {
e.preventDefault();
const modifiers = makeModifiersBitfield(e);
editor.instance.on_key_down(e.key, modifiers);
editor.instance.on_key_down(key, modifiers);
return;
}
if (dialog.dialogIsVisible()) {
if (e.key === "Escape") dialog.dismissDialog();
if (e.key === "Enter") {
if (key === "escape") dialog.dismissDialog();
if (key === "enter") {
dialog.submitDialog();
// Prevent the Enter key from acting like a click on the last clicked button, which might reopen the dialog
@ -77,10 +81,12 @@ export function createInputManager(editor: EditorState, container: HTMLElement,
};
const onKeyUp = (e: KeyboardEvent) => {
const key = getLatinKey(e);
if (shouldRedirectKeyboardEventToBackend(e)) {
e.preventDefault();
const modifiers = makeModifiersBitfield(e);
editor.instance.on_key_up(e.key, modifiers);
editor.instance.on_key_up(key, modifiers);
}
};
@ -196,3 +202,329 @@ export type InputManager = ReturnType<typeof createInputManager>;
export function makeModifiersBitfield(e: WheelEvent | PointerEvent | KeyboardEvent): number {
return Number(e.ctrlKey) | (Number(e.shiftKey) << 1) | (Number(e.altKey) << 2);
}
// This function is a naive, temporary solution to allow non-Latin keyboards to fall back on the physical QWERTY layout
function getLatinKey(e: KeyboardEvent): string {
const key = e.key.toLowerCase();
const isPrintable = isKeyPrintable(e.key);
// Control (non-printable) characters are handled normally
if (!isPrintable) return key;
// These non-Latin characters should fall back to the Latin equivalent at the key location
const LAST_LATIN_UNICODE_CHAR = 0x024f;
if (key.length > 1 || key.charCodeAt(0) > LAST_LATIN_UNICODE_CHAR) return e.code.toLowerCase();
// Otherwise, ths is a printable Latin character
return e.key.toLowerCase();
}
function isKeyPrintable(key: string): boolean {
const allPrintableKeys: string[] = [
// Modifier
"Alt",
"AltGraph",
"CapsLock",
"Control",
"Fn",
"FnLock",
"Meta",
"NumLock",
"ScrollLock",
"Shift",
"Symbol",
"SymbolLock",
// Legacy modifier
"Hyper",
"Super",
// White space
"Enter",
"Tab",
// Navigation
"ArrowDown",
"ArrowLeft",
"ArrowRight",
"ArrowUp",
"End",
"Home",
"PageDown",
"PageUp",
// Editing
"Backspace",
"Clear",
"Copy",
"CrSel",
"Cut",
"Delete",
"EraseEof",
"ExSel",
"Insert",
"Paste",
"Redo",
"Undo",
// UI
"Accept",
"Again",
"Attn",
"Cancel",
"ContextMenu",
"Escape",
"Execute",
"Find",
"Help",
"Pause",
"Play",
"Props",
"Select",
"ZoomIn",
"ZoomOut",
// Device
"BrightnessDown",
"BrightnessUp",
"Eject",
"LogOff",
"Power",
"PowerOff",
"PrintScreen",
"Hibernate",
"Standby",
"WakeUp",
// IME composition keys
"AllCandidates",
"Alphanumeric",
"CodeInput",
"Compose",
"Convert",
"Dead",
"FinalMode",
"GroupFirst",
"GroupLast",
"GroupNext",
"GroupPrevious",
"ModeChange",
"NextCandidate",
"NonConvert",
"PreviousCandidate",
"Process",
"SingleCandidate",
// Korean-specific
"HangulMode",
"HanjaMode",
"JunjaMode",
// Japanese-specific
"Eisu",
"Hankaku",
"Hiragana",
"HiraganaKatakana",
"KanaMode",
"KanjiMode",
"Katakana",
"Romaji",
"Zenkaku",
"ZenkakuHankaku",
// Common function
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
"Soft1",
"Soft2",
"Soft3",
"Soft4",
// Multimedia
"ChannelDown",
"ChannelUp",
"Close",
"MailForward",
"MailReply",
"MailSend",
"MediaClose",
"MediaFastForward",
"MediaPause",
"MediaPlay",
"MediaPlayPause",
"MediaRecord",
"MediaRewind",
"MediaStop",
"MediaTrackNext",
"MediaTrackPrevious",
"New",
"Open",
"Print",
"Save",
"SpellCheck",
// Multimedia numpad
"Key11",
"Key12",
// Audio
"AudioBalanceLeft",
"AudioBalanceRight",
"AudioBassBoostDown",
"AudioBassBoostToggle",
"AudioBassBoostUp",
"AudioFaderFront",
"AudioFaderRear",
"AudioSurroundModeNext",
"AudioTrebleDown",
"AudioTrebleUp",
"AudioVolumeDown",
"AudioVolumeUp",
"AudioVolumeMute",
"MicrophoneToggle",
"MicrophoneVolumeDown",
"MicrophoneVolumeUp",
"MicrophoneVolumeMute",
// Speech
"SpeechCorrectionList",
"SpeechInputToggle",
// Application
"LaunchApplication1",
"LaunchApplication2",
"LaunchCalendar",
"LaunchContacts",
"LaunchMail",
"LaunchMediaPlayer",
"LaunchMusicPlayer",
"LaunchPhone",
"LaunchScreenSaver",
"LaunchSpreadsheet",
"LaunchWebBrowser",
"LaunchWebCam",
"LaunchWordProcessor",
// Browser
"BrowserBack",
"BrowserFavorites",
"BrowserForward",
"BrowserHome",
"BrowserRefresh",
"BrowserSearch",
"BrowserStop",
// Mobile phone
"AppSwitch",
"Call",
"Camera",
"CameraFocus",
"EndCall",
"GoBack",
"GoHome",
"HeadsetHook",
"LastNumberRedial",
"Notification",
"MannerMode",
"VoiceDial",
// TV
"TV",
"TV3DMode",
"TVAntennaCable",
"TVAudioDescription",
"TVAudioDescriptionMixDown",
"TVAudioDescriptionMixUp",
"TVContentsMenu",
"TVDataService",
"TVInput",
"TVInputComponent1",
"TVInputComponent2",
"TVInputComposite1",
"TVInputComposite2",
"TVInputHDMI1",
"TVInputHDMI2",
"TVInputHDMI3",
"TVInputHDMI4",
"TVInputVGA1",
"TVMediaContext",
"TVNetwork",
"TVNumberEntry",
"TVPower",
"TVRadioService",
"TVSatellite",
"TVSatelliteBS",
"TVSatelliteCS",
"TVSatelliteToggle",
"TVTerrestrialAnalog",
"TVTerrestrialDigital",
"TVTimer",
// Media controls
"AVRInput",
"AVRPower",
"ColorF0Red",
"ColorF1Green",
"ColorF2Yellow",
"ColorF3Blue",
"ColorF4Grey",
"ColorF5Brown",
"ClosedCaptionToggle",
"Dimmer",
"DisplaySwap",
"DVR",
"Exit",
"FavoriteClear0",
"FavoriteClear1",
"FavoriteClear2",
"FavoriteClear3",
"FavoriteRecall0",
"FavoriteRecall1",
"FavoriteRecall2",
"FavoriteRecall3",
"FavoriteStore0",
"FavoriteStore1",
"FavoriteStore2",
"FavoriteStore3",
"Guide",
"GuideNextDay",
"GuidePreviousDay",
"Info",
"InstantReplay",
"Link",
"ListProgram",
"LiveContent",
"Lock",
"MediaApps",
"MediaAudioTrack",
"MediaLast",
"MediaSkipBackward",
"MediaSkipForward",
"MediaStepBackward",
"MediaStepForward",
"MediaTopMenu",
"NavigateIn",
"NavigateNext",
"NavigateOut",
"NavigatePrevious",
"NextFavoriteChannel",
"NextUserProfile",
"OnDemand",
"Pairing",
"PinPDown",
"PinPMove",
"PinPToggle",
"PinPUp",
"PlaySpeedDown",
"PlaySpeedReset",
"PlaySpeedUp",
"RandomToggle",
"RcLowBattery",
"RecordSpeedNext",
"RfBypass",
"ScanChannelsToggle",
"ScreenModeNext",
"Settings",
"SplitScreenToggle",
"STBInput",
"STBPower",
"Subtitle",
"Teletext",
"VideoModeNext",
"Wink",
"ZoomToggle",
];
return !allPrintableKeys.includes(key);
}