Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Work towards unifying KeyboardShortcuts and KeyBindingsDefaults #2 #7674

Merged
merged 8 commits into from
Jan 31, 2022
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
435 changes: 79 additions & 356 deletions src/KeyBindingsDefaults.ts

Large diffs are not rendered by default.

155 changes: 17 additions & 138 deletions src/KeyBindingsManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
Copyright 2021 Clemens Zeidler
Copyright 2022 Šimon Brandner <[email protected]>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -14,127 +15,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import { KeyBindingAction } from "./accessibility/KeyboardShortcuts";
import { defaultBindingsProvider } from './KeyBindingsDefaults';
import { isMac } from './Keyboard';

/** Actions for the chat message composer component */
export enum MessageComposerAction {
/** Send a message */
Send = 'KeyBinding.sendMessageInComposer',
/** Go backwards through the send history and use the message in composer view */
SelectPrevSendHistory = 'KeyBinding.previousMessageInComposerHistory',
/** Go forwards through the send history */
SelectNextSendHistory = 'KeyBinding.nextMessageInComposerHistory',
/** Start editing the user's last sent message */
EditPrevMessage = 'KeyBinding.editPreviousMessage',
/** Start editing the user's next sent message */
EditNextMessage = 'KeyBinding.editNextMessage',
/** Cancel editing a message or cancel replying to a message */
CancelEditing = 'KeyBinding.cancelReplyInComposer',

/** Set bold format the current selection */
FormatBold = 'KeyBinding.toggleBoldInComposer',
/** Set italics format the current selection */
FormatItalics = 'KeyBinding.toggleItalicsInComposer',
/** Format the current selection as quote */
FormatQuote = 'KeyBinding.toggleQuoteInComposer',
/** Undo the last editing */
EditUndo = 'KeyBinding.editUndoInComposer',
/** Redo editing */
EditRedo = 'KeyBinding.editRedoInComposer',
/** Insert new line */
NewLine = 'KeyBinding.newLineInComposer',
/** Move the cursor to the start of the message */
MoveCursorToStart = 'KeyBinding.jumpToStartInComposer',
/** Move the cursor to the end of the message */
MoveCursorToEnd = 'KeyBinding.jumpToEndInComposer',
}

/** Actions for text editing autocompletion */
export enum AutocompleteAction {
/** Accepts chosen autocomplete selection */
Complete = 'KeyBinding.completeAutocomplete',
/** Accepts chosen autocomplete selection or,
* if the autocompletion window is not shown, open the window and select the first selection */
ForceComplete = 'KeyBinding.forceCompleteAutocomplete',
/** Move to the previous autocomplete selection */
PrevSelection = 'KeyBinding.previousOptionInAutoComplete',
/** Move to the next autocomplete selection */
NextSelection = 'KeyBinding.nextOptionInAutoComplete',
/** Close the autocompletion window */
Cancel = 'KeyBinding.cancelAutoComplete',
}

/** Actions for the room list sidebar */
export enum RoomListAction {
/** Clear room list filter field */
ClearSearch = 'KeyBinding.clearRoomFilter',
/** Navigate up/down in the room list */
PrevRoom = 'KeyBinding.downerRoom',
/** Navigate down in the room list */
NextRoom = 'KeyBinding.upperRoom',
/** Select room from the room list */
SelectRoom = 'KeyBinding.selectRoomInRoomList',
/** Collapse room list section */
CollapseSection = 'KeyBinding.collapseSectionInRoomList',
/** Expand room list section, if already expanded, jump to first room in the selection */
ExpandSection = 'KeyBinding.expandSectionInRoomList',
}

/** Actions for the current room view */
export enum RoomAction {
/** Scroll up in the timeline */
ScrollUp = 'KeyBinding.scrollUpInTimeline',
/** Scroll down in the timeline */
RoomScrollDown = 'KeyBinding.scrollDownInTimeline',
/** Dismiss read marker and jump to bottom */
DismissReadMarker = 'KeyBinding.dismissReadMarkerAndJumpToBottom',
/** Jump to oldest unread message */
JumpToOldestUnread = 'KeyBinding.jumpToOldestUnreadMessage',
/** Upload a file */
UploadFile = 'KeyBinding.uploadFileToRoom',
/** Focus search message in a room (must be enabled) */
FocusSearch = 'KeyBinding.searchInRoom',
/** Jump to the first (downloaded) message in the room */
JumpToFirstMessage = 'KeyBinding.jumpToFirstMessageInTimeline',
/** Jump to the latest message in the room */
JumpToLatestMessage = 'KeyBinding.jumpToLastMessageInTimeline',
}

/** Actions for navigating do various menus, dialogs or screens */
export enum NavigationAction {
/** Jump to room search (search for a room) */
FocusRoomSearch = 'KeyBinding.filterRooms',
/** Toggle the space panel */
ToggleSpacePanel = 'KeyBinding.toggleSpacePanel',
/** Toggle the room side panel */
ToggleRoomSidePanel = 'KeyBinding.toggleRightPanel',
/** Toggle the user menu */
ToggleUserMenu = 'KeyBinding.toggleTopLeftMenu',
/** Toggle the short cut help dialog */
OpenShortCutDialog = 'KeyBinding.showKeyBindingsSettings',
/** Got to the Element home screen */
GoToHome = 'KeyBinding.goToHomeView',
/** Select prev room */
SelectPrevRoom = 'KeyBinding.previousRoom',
/** Select next room */
SelectNextRoom = 'KeyBinding.nextRoom',
/** Select prev room with unread messages */
SelectPrevUnreadRoom = 'KeyBinding.previousUnreadRoom',
/** Select next room with unread messages */
SelectNextUnreadRoom = 'KeyBinding.nextUnreadRoom',
}

/** Actions only available when labs are enabled */
export enum LabsAction {
/** Toggle visibility of hidden events */
ToggleHiddenEventVisibility = 'KeyBinding.toggleHiddenEventVisibility',
}

export type KeyBindingAction = (
MessageComposerAction | AutocompleteAction | RoomListAction | RoomAction | NavigationAction | LabsAction
);

/**
* Represent a key combination.
*
Expand All @@ -144,16 +28,16 @@ export type KeyCombo = {
key?: string;

/** On PC: ctrl is pressed; on Mac: meta is pressed */
ctrlOrCmd?: boolean;
ctrlOrCmdKey?: boolean;

altKey?: boolean;
ctrlKey?: boolean;
metaKey?: boolean;
shiftKey?: boolean;
};

export type KeyBinding<T extends string> = {
action: T;
export type KeyBinding = {
action: KeyBindingAction;
keyCombo: KeyCombo;
};

Expand Down Expand Up @@ -186,7 +70,7 @@ export function isKeyComboMatch(ev: KeyboardEvent | React.KeyboardEvent, combo:
const evShift = ev.shiftKey ?? false;
const evMeta = ev.metaKey ?? false;
// When ctrlOrCmd is set, the keys need do evaluated differently on PC and Mac
if (combo.ctrlOrCmd) {
if (combo.ctrlOrCmdKey) {
if (onMac) {
if (!evMeta
|| evCtrl !== comboCtrl
Expand Down Expand Up @@ -215,15 +99,10 @@ export function isKeyComboMatch(ev: KeyboardEvent | React.KeyboardEvent, combo:
return true;
}

export type KeyBindingGetter<T extends string> = () => KeyBinding<T>[];
export type KeyBindingGetter = () => KeyBinding[];

export interface IKeyBindingsProvider {
getMessageComposerBindings: KeyBindingGetter<MessageComposerAction>;
getAutocompleteBindings: KeyBindingGetter<AutocompleteAction>;
getRoomListBindings: KeyBindingGetter<RoomListAction>;
getRoomBindings: KeyBindingGetter<RoomAction>;
getNavigationBindings: KeyBindingGetter<NavigationAction>;
getLabsBindings: KeyBindingGetter<LabsAction>;
[key: string]: KeyBindingGetter;
}

export class KeyBindingsManager {
Expand All @@ -242,10 +121,10 @@ export class KeyBindingsManager {
/**
* Finds a matching KeyAction for a given KeyboardEvent
*/
private getAction<T extends string>(
getters: KeyBindingGetter<T>[],
private getAction(
getters: KeyBindingGetter[],
ev: KeyboardEvent | React.KeyboardEvent,
): T | undefined {
): KeyBindingAction | undefined {
for (const getter of getters) {
const bindings = getter();
const binding = bindings.find(it => isKeyComboMatch(ev, it.keyCombo, isMac));
Expand All @@ -256,27 +135,27 @@ export class KeyBindingsManager {
return undefined;
}

getMessageComposerAction(ev: KeyboardEvent | React.KeyboardEvent): MessageComposerAction | undefined {
getMessageComposerAction(ev: KeyboardEvent | React.KeyboardEvent): KeyBindingAction | undefined {
return this.getAction(this.bindingsProviders.map(it => it.getMessageComposerBindings), ev);
}

getAutocompleteAction(ev: KeyboardEvent | React.KeyboardEvent): AutocompleteAction | undefined {
getAutocompleteAction(ev: KeyboardEvent | React.KeyboardEvent): KeyBindingAction | undefined {
return this.getAction(this.bindingsProviders.map(it => it.getAutocompleteBindings), ev);
}

getRoomListAction(ev: KeyboardEvent | React.KeyboardEvent): RoomListAction | undefined {
getRoomListAction(ev: KeyboardEvent | React.KeyboardEvent): KeyBindingAction | undefined {
return this.getAction(this.bindingsProviders.map(it => it.getRoomListBindings), ev);
}

getRoomAction(ev: KeyboardEvent | React.KeyboardEvent): RoomAction | undefined {
getRoomAction(ev: KeyboardEvent | React.KeyboardEvent): KeyBindingAction | undefined {
return this.getAction(this.bindingsProviders.map(it => it.getRoomBindings), ev);
}

getNavigationAction(ev: KeyboardEvent | React.KeyboardEvent): NavigationAction | undefined {
getNavigationAction(ev: KeyboardEvent | React.KeyboardEvent): KeyBindingAction | undefined {
return this.getAction(this.bindingsProviders.map(it => it.getNavigationBindings), ev);
}

getLabsAction(ev: KeyboardEvent | React.KeyboardEvent): LabsAction | undefined {
getLabsAction(ev: KeyboardEvent | React.KeyboardEvent): KeyBindingAction | undefined {
return this.getAction(this.bindingsProviders.map(it => it.getLabsBindings), ev);
}
}
Expand Down
Loading