mirror of https://github.com/vector-im/riot-web
commit
7c4437ac9b
|
@ -161,27 +161,27 @@ const messageComposerBindings = (): KeyBinding<MessageComposerAction>[] => {
|
|||
const autocompleteBindings = (): KeyBinding<AutocompleteAction>[] => {
|
||||
return [
|
||||
{
|
||||
action: AutocompleteAction.ApplySelection,
|
||||
action: AutocompleteAction.CompleteOrNextSelection,
|
||||
keyCombo: {
|
||||
key: Key.TAB,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: AutocompleteAction.ApplySelection,
|
||||
action: AutocompleteAction.CompleteOrNextSelection,
|
||||
keyCombo: {
|
||||
key: Key.TAB,
|
||||
ctrlKey: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: AutocompleteAction.ApplySelection,
|
||||
action: AutocompleteAction.CompleteOrPrevSelection,
|
||||
keyCombo: {
|
||||
key: Key.TAB,
|
||||
shiftKey: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
action: AutocompleteAction.ApplySelection,
|
||||
action: AutocompleteAction.CompleteOrPrevSelection,
|
||||
keyCombo: {
|
||||
key: Key.TAB,
|
||||
ctrlKey: true,
|
||||
|
|
|
@ -52,14 +52,19 @@ export enum MessageComposerAction {
|
|||
|
||||
/** Actions for text editing autocompletion */
|
||||
export enum AutocompleteAction {
|
||||
/** Apply the current autocomplete selection */
|
||||
ApplySelection = 'ApplySelection',
|
||||
/** Cancel autocompletion */
|
||||
Cancel = 'Cancel',
|
||||
/**
|
||||
* Select previous selection or, if the autocompletion window is not shown, open the window and select the first
|
||||
* selection.
|
||||
*/
|
||||
CompleteOrPrevSelection = 'ApplySelection',
|
||||
/** Select next selection or, if the autocompletion window is not shown, open it and select the first selection */
|
||||
CompleteOrNextSelection = 'CompleteOrNextSelection',
|
||||
/** Move to the previous autocomplete selection */
|
||||
PrevSelection = 'PrevSelection',
|
||||
/** Move to the next autocomplete selection */
|
||||
NextSelection = 'NextSelection',
|
||||
/** Close the autocompletion window */
|
||||
Cancel = 'Cancel',
|
||||
}
|
||||
|
||||
/** Actions for the room list sidebar */
|
||||
|
|
|
@ -34,7 +34,6 @@ import { UPDATE_EVENT } from "../../stores/AsyncStore";
|
|||
import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../stores/room-list/RoomListStore";
|
||||
import {Key} from "../../Keyboard";
|
||||
import IndicatorScrollbar from "../structures/IndicatorScrollbar";
|
||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||
import { OwnProfileStore } from "../../stores/OwnProfileStore";
|
||||
|
@ -43,6 +42,7 @@ import LeftPanelWidget from "./LeftPanelWidget";
|
|||
import {replaceableComponent} from "../../utils/replaceableComponent";
|
||||
import {mediaFromMxc} from "../../customisations/Media";
|
||||
import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore";
|
||||
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
|
||||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
|
@ -297,17 +297,18 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
private onKeyDown = (ev: React.KeyboardEvent) => {
|
||||
if (!this.focusedElement) return;
|
||||
|
||||
switch (ev.key) {
|
||||
case Key.ARROW_UP:
|
||||
case Key.ARROW_DOWN:
|
||||
const action = getKeyBindingsManager().getRoomListAction(ev);
|
||||
switch (action) {
|
||||
case RoomListAction.NextRoom:
|
||||
case RoomListAction.PrevRoom:
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.onMoveFocus(ev.key === Key.ARROW_UP);
|
||||
this.onMoveFocus(action === RoomListAction.PrevRoom);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
private onEnter = () => {
|
||||
private selectRoom = () => {
|
||||
const firstRoom = this.listContainerRef.current.querySelector<HTMLDivElement>(".mx_RoomTile");
|
||||
if (firstRoom) {
|
||||
firstRoom.click();
|
||||
|
@ -388,8 +389,8 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
>
|
||||
<RoomSearch
|
||||
isMinimized={this.props.isMinimized}
|
||||
onVerticalArrow={this.onKeyDown}
|
||||
onEnter={this.onEnter}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onSelectRoom={this.selectRoom}
|
||||
/>
|
||||
<AccessibleTooltipButton
|
||||
className={classNames("mx_LeftPanel_exploreButton", {
|
||||
|
|
|
@ -444,6 +444,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
case RoomAction.RoomScrollDown:
|
||||
case RoomAction.JumpToFirstMessage:
|
||||
case RoomAction.JumpToLatestMessage:
|
||||
// pass the event down to the scroll panel
|
||||
this._onScrollKeyPressed(ev);
|
||||
handled = true;
|
||||
break;
|
||||
|
|
|
@ -30,8 +30,11 @@ import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore";
|
|||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
onVerticalArrow(ev: React.KeyboardEvent): void;
|
||||
onEnter(ev: React.KeyboardEvent): boolean;
|
||||
onKeyDown(ev: React.KeyboardEvent): void;
|
||||
/**
|
||||
* @returns true if a room has been selected and the search field should be cleared
|
||||
*/
|
||||
onSelectRoom(): boolean;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -120,10 +123,11 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
|||
break;
|
||||
case RoomListAction.NextRoom:
|
||||
case RoomListAction.PrevRoom:
|
||||
this.props.onVerticalArrow(ev);
|
||||
// we don't handle these actions here put pass the event on to the interested party (LeftPanel)
|
||||
this.props.onKeyDown(ev);
|
||||
break;
|
||||
case RoomListAction.SelectRoom: {
|
||||
const shouldClear = this.props.onEnter(ev);
|
||||
const shouldClear = this.props.onSelectRoom();
|
||||
if (shouldClear) {
|
||||
// wrap in set immediate to delay it so that we don't clear the filter & then change room
|
||||
setImmediate(() => {
|
||||
|
|
|
@ -16,10 +16,10 @@ limitations under the License.
|
|||
|
||||
import React, {createRef} from "react";
|
||||
import PropTypes from 'prop-types';
|
||||
import { Key } from '../../Keyboard';
|
||||
import Timer from '../../utils/Timer';
|
||||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||
import {replaceableComponent} from "../../utils/replaceableComponent";
|
||||
import {getKeyBindingsManager, RoomAction} from "../../KeyBindingsManager";
|
||||
|
||||
const DEBUG_SCROLL = false;
|
||||
|
||||
|
@ -535,29 +535,19 @@ export default class ScrollPanel extends React.Component {
|
|||
* @param {object} ev the keyboard event
|
||||
*/
|
||||
handleScrollKey = ev => {
|
||||
switch (ev.key) {
|
||||
case Key.PAGE_UP:
|
||||
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
this.scrollRelative(-1);
|
||||
}
|
||||
const roomAction = getKeyBindingsManager().getRoomAction(ev);
|
||||
switch (roomAction) {
|
||||
case RoomAction.ScrollUp:
|
||||
this.scrollRelative(-1);
|
||||
break;
|
||||
|
||||
case Key.PAGE_DOWN:
|
||||
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
this.scrollRelative(1);
|
||||
}
|
||||
case RoomAction.RoomScrollDown:
|
||||
this.scrollRelative(1);
|
||||
break;
|
||||
|
||||
case Key.HOME:
|
||||
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
this.scrollToTop();
|
||||
}
|
||||
case RoomAction.JumpToFirstMessage:
|
||||
this.scrollToTop();
|
||||
break;
|
||||
|
||||
case Key.END:
|
||||
if (ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||
this.scrollToBottom();
|
||||
}
|
||||
case RoomAction.JumpToLatestMessage:
|
||||
this.scrollToBottom();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -485,16 +485,14 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
if (model.autoComplete && model.autoComplete.hasCompletions()) {
|
||||
const autoComplete = model.autoComplete;
|
||||
switch (autocompleteAction) {
|
||||
case AutocompleteAction.CompleteOrPrevSelection:
|
||||
case AutocompleteAction.PrevSelection:
|
||||
autoComplete.onUpArrow(event);
|
||||
autoComplete.selectPreviousSelection();
|
||||
handled = true;
|
||||
break;
|
||||
case AutocompleteAction.CompleteOrNextSelection:
|
||||
case AutocompleteAction.NextSelection:
|
||||
autoComplete.onDownArrow(event);
|
||||
handled = true;
|
||||
break;
|
||||
case AutocompleteAction.ApplySelection:
|
||||
autoComplete.onTab(event);
|
||||
autoComplete.selectNextSelection();
|
||||
handled = true;
|
||||
break;
|
||||
case AutocompleteAction.Cancel:
|
||||
|
@ -504,8 +502,10 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
default:
|
||||
return; // don't preventDefault on anything else
|
||||
}
|
||||
} else if (autocompleteAction === AutocompleteAction.ApplySelection) {
|
||||
this.tabCompleteName(event);
|
||||
} else if (autocompleteAction === AutocompleteAction.CompleteOrPrevSelection
|
||||
|| autocompleteAction === AutocompleteAction.CompleteOrNextSelection) {
|
||||
// there is no current autocomplete window, try to open it
|
||||
this.tabCompleteName();
|
||||
handled = true;
|
||||
} else if (event.key === Key.BACKSPACE || event.key === Key.DELETE) {
|
||||
this.formatBarRef.current.hide();
|
||||
|
@ -517,7 +517,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
}
|
||||
};
|
||||
|
||||
private async tabCompleteName(event: React.KeyboardEvent) {
|
||||
private async tabCompleteName() {
|
||||
try {
|
||||
await new Promise<void>(resolve => this.setState({showVisualBell: false}, resolve));
|
||||
const {model} = this.props;
|
||||
|
@ -540,7 +540,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
|||
|
||||
// Don't try to do things with the autocomplete if there is none shown
|
||||
if (model.autoComplete) {
|
||||
await model.autoComplete.onTab(event);
|
||||
await model.autoComplete.startSelection();
|
||||
if (!model.autoComplete.hasSelection()) {
|
||||
this.setState({showVisualBell: true});
|
||||
model.autoComplete.close();
|
||||
|
|
|
@ -68,24 +68,24 @@ export default class AutocompleteWrapperModel {
|
|||
this.updateCallback({close: true});
|
||||
}
|
||||
|
||||
public async onTab(e: KeyboardEvent) {
|
||||
/**
|
||||
* If there is no current autocompletion, start one and move to the first selection.
|
||||
*/
|
||||
public async startSelection() {
|
||||
const acComponent = this.getAutocompleterComponent();
|
||||
|
||||
if (acComponent.countCompletions() === 0) {
|
||||
// Force completions to show for the text currently entered
|
||||
await acComponent.forceComplete();
|
||||
// Select the first item by moving "down"
|
||||
await acComponent.moveSelection(+1);
|
||||
} else {
|
||||
await acComponent.moveSelection(e.shiftKey ? -1 : +1);
|
||||
}
|
||||
}
|
||||
|
||||
public onUpArrow(e: KeyboardEvent) {
|
||||
public selectPreviousSelection() {
|
||||
this.getAutocompleterComponent().moveSelection(-1);
|
||||
}
|
||||
|
||||
public onDownArrow(e: KeyboardEvent) {
|
||||
public selectNextSelection() {
|
||||
this.getAutocompleterComponent().moveSelection(+1);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue