Support "produced characters" as hotkeys

"Produced characters" were not supported as hotkeys. I.e., using "!" as a hotkey was not possible.
This commit is contained in:
schroda
2026-05-13 18:57:36 +02:00
parent 4558863d19
commit bc66d52701
7 changed files with 508 additions and 486 deletions

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useHotkeys as useHotKeysHook, useHotkeysContext } from 'react-hotkeys-hook';
import { useHotkeysContext } from 'react-hotkeys-hook';
import { useEffect } from 'react';
import { HOTKEY_SCOPES } from '@/features/hotkeys/Hotkeys.constants.ts';
import { ReaderService } from '@/features/reader/services/ReaderService.ts';
@@ -30,10 +30,11 @@ import {
getReaderSettingsStore,
useReaderSettingsStore,
} from '@/features/reader/stores/ReaderStore.ts';
import { useHotkeys } from '@/lib/react-hotkeys-hook/useHotkeys.ts';
const useHotkeys = (...args: Parameters<typeof useHotKeysHook>): ReturnType<typeof useHotKeysHook> => {
const useReaderHotkeys = (...args: Parameters<typeof useHotkeys>): ReturnType<typeof useHotkeys> => {
const [keys, callback, options, dependencies] = args;
return useHotKeysHook(keys, callback, { preventDefault: true, ...options, ...HOTKEY_SCOPES.reader }, dependencies);
return useHotkeys(keys, callback, { ...options, ...HOTKEY_SCOPES.reader }, dependencies);
};
const updateSettingCycleThrough = <Setting extends keyof IReaderSettings>(
@@ -69,9 +70,9 @@ export const ReaderHotkeys = ({
const hotkeys = useReaderSettingsStore('hotkeys');
const exitReader = ReaderService.useExit();
useHotkeys(hotkeys[ReaderHotkey.PREVIOUS_PAGE], () => ReaderControls.openPage('previous'));
useHotkeys(hotkeys[ReaderHotkey.NEXT_PAGE], () => ReaderControls.openPage('next'));
useHotkeys(hotkeys[ReaderHotkey.SCROLL_BACKWARD], () => {
useReaderHotkeys(hotkeys[ReaderHotkey.PREVIOUS_PAGE], () => ReaderControls.openPage('previous'));
useReaderHotkeys(hotkeys[ReaderHotkey.NEXT_PAGE], () => ReaderControls.openPage('next'));
useReaderHotkeys(hotkeys[ReaderHotkey.SCROLL_BACKWARD], () => {
const autoScroll = getReaderAutoScrollStore();
const { readingMode, readingDirection, scrollAmount } = getReaderSettingsStore();
@@ -93,7 +94,7 @@ export const ReaderHotkeys = ({
scrollAmount,
);
});
useHotkeys(hotkeys[ReaderHotkey.SCROLL_FORWARD], () => {
useReaderHotkeys(hotkeys[ReaderHotkey.SCROLL_FORWARD], () => {
const autoScroll = getReaderAutoScrollStore();
const { readingMode, readingDirection, scrollAmount } = getReaderSettingsStore();
@@ -115,20 +116,20 @@ export const ReaderHotkeys = ({
scrollAmount,
);
});
useHotkeys(
useReaderHotkeys(
hotkeys[ReaderHotkey.PREVIOUS_CHAPTER],
() => ReaderControls.openChapter(getOptionForDirection('previous', 'next', readerThemeDirection)),
[readerThemeDirection],
);
useHotkeys(
useReaderHotkeys(
hotkeys[ReaderHotkey.NEXT_CHAPTER],
() => ReaderControls.openChapter(getOptionForDirection('next', 'previous', readerThemeDirection)),
[readerThemeDirection],
);
useHotkeys(hotkeys[ReaderHotkey.TOGGLE_MENU], () =>
useReaderHotkeys(hotkeys[ReaderHotkey.TOGGLE_MENU], () =>
getReaderOverlayStore().setIsVisible(!getReaderOverlayStore().isVisible),
);
useHotkeys(
useReaderHotkeys(
hotkeys[ReaderHotkey.CYCLE_SCALE_TYPE],
() => {
updateSettingCycleThrough(
@@ -141,13 +142,13 @@ export const ReaderHotkeys = ({
},
[],
);
useHotkeys(hotkeys[ReaderHotkey.STRETCH_IMAGE], () =>
useReaderHotkeys(hotkeys[ReaderHotkey.STRETCH_IMAGE], () =>
ReaderService.updateSetting('shouldStretchPage', !getReaderSettingsStore().shouldStretchPage.value),
);
useHotkeys(hotkeys[ReaderHotkey.OFFSET_SPREAD_PAGES], () =>
useReaderHotkeys(hotkeys[ReaderHotkey.OFFSET_SPREAD_PAGES], () =>
ReaderService.setOffsetDoubleSpreads(!getReaderSettingsStore().shouldOffsetDoubleSpreads.value),
);
useHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_MODE], () => {
useReaderHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_MODE], () => {
updateSettingCycleThrough(
'readingMode',
getReaderSettingsStore().readingMode.value,
@@ -156,7 +157,7 @@ export const ReaderHotkeys = ({
true,
);
});
useHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_DIRECTION], () => {
useReaderHotkeys(hotkeys[ReaderHotkey.CYCLE_READING_DIRECTION], () => {
updateSettingCycleThrough(
'readingDirection',
getReaderSettingsStore().readingDirection.value,
@@ -165,20 +166,20 @@ export const ReaderHotkeys = ({
true,
);
});
useHotkeys(hotkeys[ReaderHotkey.TOGGLE_AUTO_SCROLL], () => getReaderAutoScrollStore().toggleActive(), {});
useHotkeys(hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_DECREASE], () =>
useReaderHotkeys(hotkeys[ReaderHotkey.TOGGLE_AUTO_SCROLL], () => getReaderAutoScrollStore().toggleActive(), {});
useReaderHotkeys(hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_DECREASE], () =>
ReaderService.updateSetting('autoScroll', {
...getReaderSettingsStore().autoScroll,
value: Math.min(AUTO_SCROLL_SPEED.max, getReaderSettingsStore().autoScroll.value + AUTO_SCROLL_SPEED.step),
}),
);
useHotkeys(hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_INCREASE], () =>
useReaderHotkeys(hotkeys[ReaderHotkey.AUTO_SCROLL_SPEED_INCREASE], () =>
ReaderService.updateSetting('autoScroll', {
...getReaderSettingsStore().autoScroll,
value: Math.max(AUTO_SCROLL_SPEED.min, getReaderSettingsStore().autoScroll.value - AUTO_SCROLL_SPEED.step),
}),
);
useHotkeys(hotkeys[ReaderHotkey.EXIT_READER], exitReader, [exitReader]);
useReaderHotkeys(hotkeys[ReaderHotkey.EXIT_READER], exitReader, [exitReader]);
useEffect(() => {
enableScope(HotkeyScope.READER);

View File

@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import Stack from '@mui/material/Stack';
import { useRecordHotkeys } from 'react-hotkeys-hook';
import Dialog from '@mui/material/Dialog';
@@ -17,6 +17,7 @@ import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import { Trans, useLingui } from '@lingui/react/macro';
import { Hotkey } from '@/features/reader/hotkeys/settings/components/Hotkey.tsx';
import { CheckboxInput } from '@/base/components/inputs/CheckboxInput.tsx';
export const RecordHotkey = ({
onClose,
@@ -29,7 +30,10 @@ export const RecordHotkey = ({
}) => {
const { t } = useLingui();
const [recordedKeys, { start, stop, resetKeys }] = useRecordHotkeys();
const [recordPhysicalKeys, setRecordPhysicalKeys] = useState(true);
const [recordedKeys, { start, stop, resetKeys }] = useRecordHotkeys(!recordPhysicalKeys);
const keys = [[...recordedKeys].join('+')];
const isExistingKey = keys.some((key) =>
existingKeys.map((existingKey) => existingKey.toLowerCase()).includes(key.toLowerCase()),
@@ -41,7 +45,7 @@ export const RecordHotkey = ({
return () => {
stop();
};
}, []);
}, [start, stop]);
return (
<Dialog open onClose={onClose} fullWidth>
@@ -54,6 +58,32 @@ export const RecordHotkey = ({
</Trans>
</Stack>
{isExistingKey && <Typography color="error">{t`Hotkey already exists`}</Typography>}
<CheckboxInput
label={
<Stack
sx={{
// Padding comes from the MUI Checkbox component
pt: '9px',
}}
>
<Typography>{t`Record physical keys`}</Typography>
<Typography variant="body2" color="textSecondary" sx={{ display: 'flex', gap: 1 }}>
<Trans>
enabled: records <Hotkey keys={['shift+1']} /> when the user presses Shift+1
</Trans>
</Typography>
<Typography variant="body2" color="textSecondary" sx={{ display: 'flex', gap: 1 }}>
<Trans>
disabled: records <Hotkey keys={['!']} /> when the user presses Shift+1 on a US
layout
</Trans>
</Typography>
</Stack>
}
checked={recordPhysicalKeys}
onChange={(_, checked) => setRecordPhysicalKeys(checked)}
sx={{ mt: 2, alignItems: 'start' }}
/>
</DialogContent>
<DialogActions>
<Stack