impr(keymap): dynamic legends now show alt layer if supported by the layout

This commit is contained in:
Miodec 2025-03-27 14:29:58 +01:00
parent 6513e6481a
commit cfc810a7d8
4 changed files with 159 additions and 108 deletions

View file

@ -9,6 +9,10 @@ import * as Hangul from "hangul-js";
import * as Notifications from "../elements/notifications";
import * as ActivePage from "../states/active-page";
import * as TestWords from "../test/test-words";
import { capsState } from "../test/caps-warning";
import * as ShiftTracker from "../test/shift-tracker";
import * as AltTracker from "../test/alt-tracker";
import * as KeyConverter from "../utils/key-converter";
const stenoKeys: JSONData.Layout = {
keymapShowTopRow: true,
@ -402,6 +406,112 @@ export async function refresh(
}
}
const isMacLike = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
const symbolsPattern = /^[^\p{L}\p{N}]{1}$/u;
type KeymapLegendStates = [letters: 0 | 1 | 2 | 3, symbols: 0 | 1 | 2 | 3];
let keymapLegendStates: KeymapLegendStates = [0, 0];
function getLegendStates(): KeymapLegendStates | undefined {
// MacOS has different CapsLock and Shift logic than other operating systems
// Windows and Linux only capitalize letters if either Shift OR CapsLock are
// pressed, but not both at once.
// MacOS instead capitalizes when either or both are pressed,
// so we have to check for that.
const shiftState = ShiftTracker.leftState || ShiftTracker.rightState;
const altState = AltTracker.leftState || AltTracker.rightState;
const osDependentLettersState = isMacLike
? shiftState || capsState
: shiftState !== capsState;
const lettersState = (osDependentLettersState ? 1 : 0) + (altState ? 2 : 0);
const symbolsState = (shiftState ? 1 : 0) + (altState ? 2 : 0);
const [previousLettersState, previousSymbolsState] = keymapLegendStates;
if (
previousLettersState === lettersState &&
previousSymbolsState === symbolsState
) {
return;
}
keymapLegendStates = [
lettersState as 0 | 1 | 2 | 3,
symbolsState as 0 | 1 | 2 | 3,
];
return keymapLegendStates;
}
async function updateLegends(): Promise<void> {
const states = getLegendStates();
if (states === undefined) return;
const keymapKeys = [...document.getElementsByClassName("keymapKey")].filter(
(el) => {
const isKeymapKey = el.classList.contains("keymapKey");
const isNotSpace = !el.classList.contains("keySpace");
return isKeymapKey && isNotSpace;
}
) as HTMLElement[];
const layoutKeys = keymapKeys.map((el) => el.dataset["key"]);
if (layoutKeys.includes(undefined)) return;
const keys = keymapKeys.map((el) => el.childNodes[1]);
const [lettersState, symbolsState] = states;
const layoutName =
Config.keymapLayout === "overrideSync"
? Config.layout === "default"
? "qwerty"
: Config.layout
: Config.keymapLayout;
const layout = await JSONData.getLayout(layoutName).catch(() => undefined);
if (layout === undefined) {
Notifications.add("Failed to load keymap layout", -1);
return;
}
for (let i = 0; i < layoutKeys.length; i++) {
const layoutKey = layoutKeys[i] as string;
const key = keys[i];
const lowerCaseCharacter = layoutKey[0];
const upperCaseCharacter = layoutKey[1];
if (
key === undefined ||
layoutKey === undefined ||
lowerCaseCharacter === undefined ||
upperCaseCharacter === undefined
)
continue;
const keyIsSymbol = [lowerCaseCharacter, upperCaseCharacter].some(
(character) => symbolsPattern.test(character ?? "")
);
const keycode = KeyConverter.layoutKeyToKeycode(lowerCaseCharacter, layout);
if (keycode === undefined) {
return;
}
const oppositeShift = ShiftTracker.isUsingOppositeShift(keycode);
const state = keyIsSymbol ? symbolsState : lettersState;
const characterIndex = oppositeShift ? state : 0;
//if the character at the index is undefined, try without alt
const character =
layoutKey[characterIndex] ?? layoutKey[characterIndex - 2];
key.textContent = character ?? "";
}
}
ConfigEvent.subscribe((eventKey, newValue) => {
if (eventKey === "layout" && Config.keymapLayout === "overrideSync") {
void refresh(Config.keymapLayout);
@ -427,3 +537,27 @@ KeymapEvent.subscribe((mode, key, correct) => {
void flashKey(key, correct);
}
});
$(document).on("keydown", (e) => {
if (
Config.keymapLegendStyle === "dynamic" &&
(e.code === "ShiftLeft" ||
e.code === "ShiftRight" ||
e.code === "AltRight" ||
e.code === "AltLeft")
) {
void updateLegends();
}
});
$(document).on("keyup", (e) => {
if (
Config.keymapLegendStyle === "dynamic" &&
(e.code === "ShiftLeft" ||
e.code === "ShiftRight" ||
e.code === "AltRight" ||
e.code === "AltLeft")
) {
void updateLegends();
}
});

View file

@ -0,0 +1,23 @@
export let leftState = false;
export let rightState = false;
$(document).on("keydown", (e) => {
if (e.code === "AltLeft") {
leftState = true;
} else if (e.code === "AltRight") {
rightState = true;
}
});
$(document).on("keyup", (e) => {
if (e.code === "AltLeft") {
leftState = false;
} else if (e.code === "AltRight") {
rightState = false;
}
});
export function reset(): void {
leftState = false;
rightState = false;
}

View file

@ -1,109 +1,9 @@
import Config from "../config";
import * as JSONData from "../utils/json-data";
import { capsState } from "./caps-warning";
import * as Notifications from "../elements/notifications";
import * as KeyConverter from "../utils/key-converter";
export let leftState = false;
export let rightState = false;
type KeymapLegendStates = [letters: boolean, symbols: boolean];
const symbolsPattern = /^[^\p{L}\p{N}]{1}$/u;
const isMacLike = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
let keymapLegendStates: KeymapLegendStates = [false, false];
function getLegendStates(): KeymapLegendStates | undefined {
const symbolsState = leftState || rightState;
// MacOS has different CapsLock and Shift logic than other operating systems
// Windows and Linux only capitalize letters if either Shift OR CapsLock are
// pressed, but not both at once.
// MacOS instead capitalizes when either or both are pressed,
// so we have to check for that.
const lettersState = isMacLike
? symbolsState || capsState
: symbolsState !== capsState;
const [previousLettersState, previousSymbolsState] = keymapLegendStates;
if (
previousLettersState === lettersState &&
previousSymbolsState === symbolsState
) {
return;
}
return (keymapLegendStates = [lettersState, symbolsState]);
}
async function updateKeymapLegendCasing(): Promise<void> {
const states = getLegendStates();
if (states === undefined) return;
const keymapKeys = [...document.getElementsByClassName("keymapKey")].filter(
(el) => {
const isKeymapKey = el.classList.contains("keymapKey");
const isNotSpace = !el.classList.contains("keySpace");
return isKeymapKey && isNotSpace;
}
) as HTMLElement[];
const layoutKeys = keymapKeys.map((el) => el.dataset["key"]);
if (layoutKeys.includes(undefined)) return;
const keys = keymapKeys.map((el) => el.childNodes[1]);
const [lettersState, symbolsState] = states;
const layoutName =
Config.keymapLayout === "overrideSync"
? Config.layout === "default"
? "qwerty"
: Config.layout
: Config.keymapLayout;
const layout = await JSONData.getLayout(layoutName).catch(() => undefined);
if (layout === undefined) {
Notifications.add("Failed to load keymap layout", -1);
return;
}
for (let i = 0; i < layoutKeys.length; i++) {
const layoutKey = layoutKeys[i] as string;
const key = keys[i];
const lowerCaseCharacter = layoutKey[0];
const upperCaseCharacter = layoutKey[1];
if (
key === undefined ||
layoutKey === undefined ||
lowerCaseCharacter === undefined ||
upperCaseCharacter === undefined
)
continue;
const keyIsSymbol = [lowerCaseCharacter, upperCaseCharacter].some(
(character) => symbolsPattern.test(character ?? "")
);
const keycode = KeyConverter.layoutKeyToKeycode(lowerCaseCharacter, layout);
if (keycode === undefined) {
return;
}
const oppositeShift = isUsingOppositeShift(keycode);
const state = keyIsSymbol ? symbolsState : lettersState;
const capitalize = oppositeShift && state;
const keyIndex = Number(capitalize);
const character = layoutKey[keyIndex];
key.textContent = character ?? "";
}
}
$(document).on("keydown", (e) => {
if (e.code === "ShiftLeft") {
leftState = true;
@ -112,10 +12,6 @@ $(document).on("keydown", (e) => {
leftState = false;
rightState = true;
}
if (Config.keymapLegendStyle === "dynamic") {
void updateKeymapLegendCasing();
}
});
$(document).on("keyup", (e) => {
@ -123,10 +19,6 @@ $(document).on("keyup", (e) => {
leftState = false;
rightState = false;
}
if (Config.keymapLegendStyle === "dynamic") {
void updateKeymapLegendCasing();
}
});
export function reset(): void {

View file

@ -13,6 +13,7 @@ import * as CustomTextState from "../states/custom-text-name";
import * as TestStats from "./test-stats";
import * as PractiseWords from "./practise-words";
import * as ShiftTracker from "./shift-tracker";
import * as AltTracker from "./alt-tracker";
import * as Focus from "./focus";
import * as Funbox from "./funbox/funbox";
import * as Keymap from "../elements/keymap";
@ -255,6 +256,7 @@ export function restart(options = {} as RestartOptions): void {
TestInput.restart();
TestInput.corrected.reset();
ShiftTracker.reset();
AltTracker.reset();
Caret.hide();
TestState.setActive(false);
Replay.stopReplayRecording();