Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enables keyboard to use multiple of the same options across buttons #1271

Merged
merged 4 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 5 additions & 20 deletions www/src/Addons/Keyboard.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import React, { useContext, useState } from 'react';
import React, { useContext } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { FormCheck, Row, FormLabel } from 'react-bootstrap';
import { NavLink } from 'react-router-dom';
import * as yup from 'yup';

import Section from '../Components/Section';

import FormControl from '../Components/FormControl';
import FormSelect from '../Components/FormSelect';
import KeyboardMapper, { validateMappings } from '../Components/KeyboardMapper';
import KeyboardMapper from '../Components/KeyboardMapper';
import { baseButtonMappings } from '../Services/WebApi';
import { AppContext } from '../Contexts/AppContext';

import {
BUTTON_ACTIONS,
PIN_DIRECTIONS,
PinActionValues,
PinDirectionValues,
} from '../Data/Pins';
import { BUTTON_ACTIONS } from '../Data/Pins';
import { BUTTON_MASKS_OPTIONS } from '../Data/Buttons';

export const keyboardScheme = {
Expand Down Expand Up @@ -56,11 +50,6 @@ export const keyboardState = {
KeyboardHostAddonEnabled: 0,
};

const options = Object.entries(BUTTON_ACTIONS).map(([key, value]) => ({
label: key,
value,
}));

const excludedButtons = [
'E1',
'E2',
Expand All @@ -85,14 +74,11 @@ const Keyboard = ({
}) => {
const { buttonLabels, getAvailablePeripherals } = useContext(AppContext);
const { t } = useTranslation();
const [validated, setValidated] = useState(false);

const handleKeyChange = (values, setFieldValue) => (value, button) => {
const newMappings = { ...values.keyboardHostMap };
newMappings[button].key = value;
const mappings = validateMappings(newMappings, t);
setFieldValue('keyboardHostMap', mappings);
setValidated(true);
setFieldValue('keyboardHostMap', newMappings);
};

const getKeyMappingForButton = (values) => (button) => {
Expand All @@ -114,7 +100,6 @@ const Keyboard = ({
buttonLabels={buttonLabels}
excludeButtons={excludedButtons}
handleKeyChange={handleKeyChange(values, setFieldValue)}
validated={validated}
getKeyMappingForButton={getKeyMappingForButton(values)}
/>
</div>
Expand Down Expand Up @@ -194,7 +179,7 @@ const Keyboard = ({
i18nKey="peripheral-toggle-unavailable"
values={{ name: 'USB' }}
>
<NavLink exact="true" to="/peripheral-mapping">
<NavLink to="/peripheral-mapping">
{t('PeripheralMapping:header-text')}
</NavLink>
</Trans>
Expand Down
2 changes: 1 addition & 1 deletion www/src/Components/ContextualHelpOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Offcanvas from 'react-bootstrap/Offcanvas';

import InfoCircle from '../Icons/InfoCircle';

const ContextualHelpOverlay = ({ name, ...props }) => {
const ContextualHelpOverlay = ({ ...props }) => {
const [show, setShow] = useState(false);

const handleClose = () => setShow(false);
Expand Down
54 changes: 3 additions & 51 deletions www/src/Components/KeyboardMapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,19 @@ import { useTranslation } from 'react-i18next';
import FormSelect from './FormSelect';
import { KEY_CODES } from '../Data/Keyboard';
import { BUTTONS } from '../Data/Buttons';
import boards from '../Data/Boards.json';
import cloneDeep from 'lodash/cloneDeep';

const KeyboardMapper = ({
buttonLabels,
handleKeyChange,
validated,
getKeyMappingForButton,
excludeButtons,
...props
excludeButtons = [''],
}) => {
const { buttonLabelType, swapTpShareLabels } = buttonLabels;

const { t } = useTranslation('Components', { keyPrefix: 'keyboard-mapper' });

return (
<table className="table table-sm pin-mapping-table" {...props}>
<table className="table table-sm pin-mapping-table">
<thead className="table">
<tr>
<th className="table-header-button-label">
Expand Down Expand Up @@ -50,20 +46,12 @@ const KeyboardMapper = ({
}
const keyMapping = getKeyMappingForButton(button);
return (
<tr
key={`button-map-${i}`}
className={
validated && !!keyMapping.error ? 'table-danger' : ''
}
>
<tr key={`button-map-${i}`}>
<td>{label}</td>
<td>
<FormSelect
type="number"
className="form-select-sm sm-3"
value={keyMapping.key}
isInvalid={!!keyMapping.error}
error={keyMapping.error}
onChange={(e) =>
handleKeyChange(
e.target.value ? parseInt(e.target.value) : '',
Expand All @@ -86,40 +74,4 @@ const KeyboardMapper = ({
);
};

export const validateMappings = (keyMappings, t) => {
const mappings = cloneDeep(keyMappings);
const props = Object.keys(mappings);

for (let prop of props) {
mappings[prop].error = null;

for (let otherProp of props) {
if (prop === otherProp) continue;

const key = KEY_CODES.find(
({ value }) => mappings?.[prop]?.key === value,
)?.label;

if (
mappings[prop].key !== 0x00 &&
mappings[prop].key === mappings[otherProp].key
) {
mappings[prop].error = t('Components:keyboard-mapper.error-conflict', {
key,
});
} else if (
(boards[import.meta.env.VITE_GP2040_BOARD].invalidKeys || []).filter(
(p) => p === mappings[prop].key,
).length > 0
) {
mappings[prop].error = t('Components:keyboard-mapper.error-invalid', {
key,
});
}
}
}

return mappings;
};

export default KeyboardMapper;
1 change: 0 additions & 1 deletion www/src/Data/Buttons.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,6 @@ export const BUTTONS = {
A2: 'Touchpad Center',
A3: 'Touchpad Left',
A4: 'Touchpad Right',
A2: 'Touchpad',
E1: '-',
E2: '-',
E3: '-',
Expand Down
2 changes: 0 additions & 2 deletions www/src/Locales/de-DE/Components.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,5 @@ export default {
},
'keyboard-mapper': {
'key-header': 'Taste',
'error-conflict': 'Die Taste {{key}} ist bereits zugewiesen',
'error-invalid': '{{key}} ist für dieses Board ungültig',
},
};
2 changes: 0 additions & 2 deletions www/src/Locales/en/Components.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,5 @@ export default {
},
'keyboard-mapper': {
'key-header': 'Key',
'error-conflict': 'Key {{key}} is already assigned',
'error-invalid': '{{key}} is invalid for this board',
},
};
2 changes: 0 additions & 2 deletions www/src/Locales/ja-JP/Components.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,5 @@ export default {
},
'keyboard-mapper': {
'key-header': 'キー',
'error-conflict': '{{key}} キーは既に割り当て済みです',
'error-invalid': 'このボードでは {{key}} は無効です',
},
};
4 changes: 1 addition & 3 deletions www/src/Locales/ko-KR/Components.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default {
'zh-CN': '简体中文 (중국어 간체)',
'de-DE': 'Deutsch (독일어)',
'ja-JP': '日本語 (일본어)',
'ko-KR': '한국어 (Korean)',
'ko-KR': '한국어 (Korean)',
},
'color-scheme': {
dark: '다크',
Expand All @@ -15,7 +15,5 @@ export default {
},
'keyboard-mapper': {
'key-header': '키',
'error-conflict': '{{key}}키는 이미 할당되어 있습니다.',
'error-invalid': '{{key}}키는 현재 보드에는 사용 할 수 없습니다.',
},
};
2 changes: 0 additions & 2 deletions www/src/Locales/pt-BR/Components.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,5 @@ export default {
},
'keyboard-mapper': {
'key-header': 'Tecla',
'error-conflict': 'A tecla {{key}} já está atribuída',
'error-invalid': '{{key}} é inválida para esta placa',
},
};
2 changes: 0 additions & 2 deletions www/src/Locales/zh-CN/Components.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,5 @@ export default {
},
'keyboard-mapper': {
'key-header': '按键',
'error-conflict': '按键 {{key}} 已分配',
'error-invalid': '{{key}} 对该电路板无效',
},
};
3 changes: 2 additions & 1 deletion www/src/Pages/PeripheralMappingPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export default function PeripheralMappingPage() {
);
return (
<Table
key={`details-${i}`}
className="caption-top"
striped="columns"
responsive
Expand Down Expand Up @@ -246,7 +247,7 @@ export default function PeripheralMappingPage() {
? o
: `${o} - ${t(
'PeripheralMapping:pin-in-use',
)}`}
)}`}
</option>
))}
</FormSelect>
Expand Down
19 changes: 3 additions & 16 deletions www/src/Pages/SettingsPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import useProfilesStore from '../Store/useProfilesStore';
import { AppContext } from '../Contexts/AppContext';

import ContextualHelpOverlay from '../Components/ContextualHelpOverlay';
import KeyboardMapper, { validateMappings } from '../Components/KeyboardMapper';
import KeyboardMapper from '../Components/KeyboardMapper';
import Section from '../Components/Section';
import WebApi, { baseButtonMappings } from '../Services/WebApi';
import { BUTTON_MASKS_OPTIONS, getButtonLabels } from '../Data/Buttons';
Expand Down Expand Up @@ -99,7 +99,7 @@ const SHA256 = (ascii) => {
(rightRotate(w15, 7) ^ rightRotate(w15, 18) ^ (w15 >>> 3)) + // s0
w[i - 7] +
(rightRotate(w2, 17) ^ rightRotate(w2, 19) ^ (w2 >>> 10))) | // s1
0);
0);
// This is only used once, so *could* be moved below, but it only saves 4 bytes and makes things unreadble
const temp2 =
(rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22)) + // S0
Expand Down Expand Up @@ -651,9 +651,7 @@ export default function SettingsPage() {
const handleKeyChange = (value, button) => {
const newMappings = { ...keyMappings };
newMappings[button].key = value;
const mappings = validateMappings(newMappings, t);
setKeyMappings(mappings);
setValidated(true);
setKeyMappings(newMappings);
};

const generateAuthSelection = (
Expand Down Expand Up @@ -721,7 +719,6 @@ export default function SettingsPage() {
<KeyboardMapper
buttonLabels={buttonLabels}
handleKeyChange={handleKeyChange}
validated={validated}
getKeyMappingForButton={getKeyMappingForButton}
/>
</div>
Expand Down Expand Up @@ -1261,16 +1258,6 @@ export default function SettingsPage() {
const onSubmit = async (values) => {
const isKeyboardMode = values.inputMode === 3;

if (isKeyboardMode) {
const mappings = validateMappings(keyMappings, t);
setKeyMappings(mappings);
setValidated(true);
if (Object.keys(mappings).some((p) => !!mappings[p].error)) {
setSaveMessage(t('Common:errors.validation-error'));
return;
}
}

const data = {
...values,
usbProductID: hexToInt(values.usbProductID || '0000'),
Expand Down