131
src/accessibility/KeyboardShortcutUtils.ts
Normal file
131
src/accessibility/KeyboardShortcutUtils.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Copyright 2022 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { KeyCombo } from "../KeyBindingsManager";
|
||||
import { isMac, Key } from "../Keyboard";
|
||||
import { _t, _td } from "../languageHandler";
|
||||
import PlatformPeg from "../PlatformPeg";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import {
|
||||
DESKTOP_SHORTCUTS,
|
||||
DIGITS,
|
||||
IKeyboardShortcuts,
|
||||
KeyBindingAction,
|
||||
KEYBOARD_SHORTCUTS,
|
||||
MAC_ONLY_SHORTCUTS,
|
||||
} from "./KeyboardShortcuts";
|
||||
|
||||
/**
|
||||
* This function gets the keyboard shortcuts that should be presented in the UI
|
||||
* but they shouldn't be consumed by KeyBindingDefaults. That means that these
|
||||
* have to be manually mirrored in KeyBindingDefaults.
|
||||
*/
|
||||
const getUIOnlyShortcuts = (): IKeyboardShortcuts => {
|
||||
const ctrlEnterToSend = SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend');
|
||||
|
||||
const keyboardShortcuts: IKeyboardShortcuts = {
|
||||
[KeyBindingAction.SendMessage]: {
|
||||
default: {
|
||||
key: Key.ENTER,
|
||||
ctrlOrCmdKey: ctrlEnterToSend,
|
||||
},
|
||||
displayName: _td("Send message"),
|
||||
},
|
||||
[KeyBindingAction.NewLine]: {
|
||||
default: {
|
||||
key: Key.ENTER,
|
||||
shiftKey: !ctrlEnterToSend,
|
||||
},
|
||||
displayName: _td("New line"),
|
||||
},
|
||||
[KeyBindingAction.CompleteAutocomplete]: {
|
||||
default: {
|
||||
key: Key.ENTER,
|
||||
},
|
||||
displayName: _td("Complete"),
|
||||
},
|
||||
[KeyBindingAction.ForceCompleteAutocomplete]: {
|
||||
default: {
|
||||
key: Key.TAB,
|
||||
},
|
||||
displayName: _td("Force complete"),
|
||||
},
|
||||
[KeyBindingAction.SearchInRoom]: {
|
||||
default: {
|
||||
ctrlOrCmdKey: true,
|
||||
key: Key.F,
|
||||
},
|
||||
displayName: _td("Search (must be enabled)"),
|
||||
},
|
||||
};
|
||||
|
||||
if (PlatformPeg.get().overrideBrowserShortcuts()) {
|
||||
// XXX: This keyboard shortcut isn't manually added to
|
||||
// KeyBindingDefaults as it can't be easily handled by the
|
||||
// KeyBindingManager
|
||||
keyboardShortcuts[KeyBindingAction.SwitchToSpaceByNumber] = {
|
||||
default: {
|
||||
ctrlOrCmdKey: true,
|
||||
key: DIGITS,
|
||||
},
|
||||
displayName: _td("Switch to space by number"),
|
||||
};
|
||||
}
|
||||
|
||||
return keyboardShortcuts;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function gets keyboard shortcuts that can be consumed by the KeyBindingDefaults.
|
||||
*/
|
||||
export const getKeyboardShortcuts = (): IKeyboardShortcuts => {
|
||||
const overrideBrowserShortcuts = PlatformPeg.get().overrideBrowserShortcuts();
|
||||
|
||||
return Object.keys(KEYBOARD_SHORTCUTS).filter((k: KeyBindingAction) => {
|
||||
if (KEYBOARD_SHORTCUTS[k]?.controller?.settingDisabled) return false;
|
||||
if (MAC_ONLY_SHORTCUTS.includes(k) && !isMac) return false;
|
||||
if (DESKTOP_SHORTCUTS.includes(k) && !overrideBrowserShortcuts) return false;
|
||||
|
||||
return true;
|
||||
}).reduce((o, key) => {
|
||||
o[key] = KEYBOARD_SHORTCUTS[key];
|
||||
return o;
|
||||
}, {} as IKeyboardShortcuts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets keyboard shortcuts that should be presented to the user in the UI.
|
||||
*/
|
||||
export const getKeyboardShortcutsForUI = (): IKeyboardShortcuts => {
|
||||
const entries = [
|
||||
...Object.entries(getUIOnlyShortcuts()),
|
||||
...Object.entries(getKeyboardShortcuts()),
|
||||
];
|
||||
|
||||
return entries.reduce((acc, [key, value]) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {} as IKeyboardShortcuts);
|
||||
};
|
||||
|
||||
export const getKeyboardShortcutValue = (name: string): KeyCombo => {
|
||||
return getKeyboardShortcutsForUI()[name]?.default;
|
||||
};
|
||||
|
||||
export const getKeyboardShortcutDisplayName = (name: string): string | null => {
|
||||
const keyboardShortcutDisplayName = getKeyboardShortcutsForUI()[name]?.displayName;
|
||||
return keyboardShortcutDisplayName && _t(keyboardShortcutDisplayName);
|
||||
};
|
||||
@@ -18,9 +18,8 @@ limitations under the License.
|
||||
import { _td } from "../languageHandler";
|
||||
import { isMac, Key } from "../Keyboard";
|
||||
import { IBaseSetting } from "../settings/Settings";
|
||||
import SettingsStore from "../settings/SettingsStore";
|
||||
import IncompatibleController from "../settings/controllers/IncompatibleController";
|
||||
import PlatformPeg from "../PlatformPeg";
|
||||
import { KeyCombo } from "../KeyBindingsManager";
|
||||
|
||||
export enum KeyBindingAction {
|
||||
/** Send a message */
|
||||
@@ -151,18 +150,9 @@ export enum KeyBindingAction {
|
||||
ToggleHiddenEventVisibility = 'KeyBinding.toggleHiddenEventVisibility',
|
||||
}
|
||||
|
||||
export type KeyBindingConfig = {
|
||||
key: string;
|
||||
ctrlOrCmdKey?: boolean;
|
||||
ctrlKey?: boolean;
|
||||
altKey?: boolean;
|
||||
shiftKey?: boolean;
|
||||
metaKey?: boolean;
|
||||
};
|
||||
type KeyboardShortcutSetting = IBaseSetting<KeyCombo>;
|
||||
|
||||
type KeyboardShortcutSetting = IBaseSetting<KeyBindingConfig>;
|
||||
|
||||
type IKeyboardShortcuts = {
|
||||
export type IKeyboardShortcuts = {
|
||||
// TODO: We should figure out what to do with the keyboard shortcuts that are not handled by KeybindingManager
|
||||
[k in (KeyBindingAction)]?: KeyboardShortcutSetting;
|
||||
};
|
||||
@@ -310,14 +300,14 @@ export const CATEGORIES: Record<CategoryName, ICategory> = {
|
||||
},
|
||||
};
|
||||
|
||||
const DESKTOP_SHORTCUTS = [
|
||||
export const DESKTOP_SHORTCUTS = [
|
||||
KeyBindingAction.OpenUserSettings,
|
||||
KeyBindingAction.SwitchToSpaceByNumber,
|
||||
KeyBindingAction.PreviousVisitedRoomOrCommunity,
|
||||
KeyBindingAction.NextVisitedRoomOrCommunity,
|
||||
];
|
||||
|
||||
const MAC_ONLY_SHORTCUTS = [
|
||||
export const MAC_ONLY_SHORTCUTS = [
|
||||
KeyBindingAction.OpenUserSettings,
|
||||
];
|
||||
|
||||
@@ -710,99 +700,6 @@ export const KEYBOARD_SHORTCUTS: IKeyboardShortcuts = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* This function gets the keyboard shortcuts that should be presented in the UI
|
||||
* but they shouldn't be consumed by KeyBindingDefaults. That means that these
|
||||
* have to be manually mirrored in KeyBindingDefaults.
|
||||
*/
|
||||
const getUIOnlyShortcuts = (): IKeyboardShortcuts => {
|
||||
const ctrlEnterToSend = SettingsStore.getValue('MessageComposerInput.ctrlEnterToSend');
|
||||
|
||||
const keyboardShortcuts: IKeyboardShortcuts = {
|
||||
[KeyBindingAction.SendMessage]: {
|
||||
default: {
|
||||
key: Key.ENTER,
|
||||
ctrlOrCmdKey: ctrlEnterToSend,
|
||||
},
|
||||
displayName: _td("Send message"),
|
||||
},
|
||||
[KeyBindingAction.NewLine]: {
|
||||
default: {
|
||||
key: Key.ENTER,
|
||||
shiftKey: !ctrlEnterToSend,
|
||||
},
|
||||
displayName: _td("New line"),
|
||||
},
|
||||
[KeyBindingAction.CompleteAutocomplete]: {
|
||||
default: {
|
||||
key: Key.ENTER,
|
||||
},
|
||||
displayName: _td("Complete"),
|
||||
},
|
||||
[KeyBindingAction.ForceCompleteAutocomplete]: {
|
||||
default: {
|
||||
key: Key.TAB,
|
||||
},
|
||||
displayName: _td("Force complete"),
|
||||
},
|
||||
[KeyBindingAction.SearchInRoom]: {
|
||||
default: {
|
||||
ctrlOrCmdKey: true,
|
||||
key: Key.F,
|
||||
},
|
||||
displayName: _td("Search (must be enabled)"),
|
||||
},
|
||||
};
|
||||
|
||||
if (PlatformPeg.get().overrideBrowserShortcuts()) {
|
||||
// XXX: This keyboard shortcut isn't manually added to
|
||||
// KeyBindingDefaults as it can't be easily handled by the
|
||||
// KeyBindingManager
|
||||
keyboardShortcuts[KeyBindingAction.SwitchToSpaceByNumber] = {
|
||||
default: {
|
||||
ctrlOrCmdKey: true,
|
||||
key: DIGITS,
|
||||
},
|
||||
displayName: _td("Switch to space by number"),
|
||||
};
|
||||
}
|
||||
|
||||
return keyboardShortcuts;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function gets keyboard shortcuts that can be consumed by the KeyBindingDefaults.
|
||||
*/
|
||||
export const getKeyboardShortcuts = (): IKeyboardShortcuts => {
|
||||
const overrideBrowserShortcuts = PlatformPeg.get().overrideBrowserShortcuts();
|
||||
|
||||
return Object.keys(KEYBOARD_SHORTCUTS).filter((k: KeyBindingAction) => {
|
||||
if (KEYBOARD_SHORTCUTS[k]?.controller?.settingDisabled) return false;
|
||||
if (MAC_ONLY_SHORTCUTS.includes(k) && !isMac) return false;
|
||||
if (DESKTOP_SHORTCUTS.includes(k) && !overrideBrowserShortcuts) return false;
|
||||
|
||||
return true;
|
||||
}).reduce((o, key) => {
|
||||
o[key] = KEYBOARD_SHORTCUTS[key];
|
||||
return o;
|
||||
}, {} as IKeyboardShortcuts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets keyboard shortcuts that should be presented to the user in the UI.
|
||||
*/
|
||||
export const getKeyboardShortcutsForUI = (): IKeyboardShortcuts => {
|
||||
const entries = [
|
||||
...Object.entries(getUIOnlyShortcuts()),
|
||||
...Object.entries(getKeyboardShortcuts()),
|
||||
];
|
||||
|
||||
return entries.reduce((acc, [key, value]) => {
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {} as IKeyboardShortcuts);
|
||||
};
|
||||
|
||||
// For tests
|
||||
export function mock({ keyboardShortcuts, macOnlyShortcuts, desktopShortcuts }): void {
|
||||
Object.keys(KEYBOARD_SHORTCUTS).forEach((k) => delete KEYBOARD_SHORTCUTS[k]);
|
||||
|
||||
Reference in New Issue
Block a user