From f9be3a9ac8609858c4b84cfcdc623301ee7d5a23 Mon Sep 17 00:00:00 2001 From: Alan Ren Date: Thu, 29 Jun 2023 17:11:51 -0700 Subject: [PATCH] fix slickgrid keyboard event handler (#23575) * fix slickgrid keyboard events * add back some code --- src/sql/base/browser/dom.ts | 7 +++++ .../plugins/additionalKeyBindings.plugin.ts | 22 ++++++++-------- .../plugins/cellSelectionModel.plugin.ts | 26 ++++++++----------- .../ui/table/plugins/checkboxColumn.plugin.ts | 8 +++--- .../plugins/checkboxSelectColumn.plugin.ts | 10 +++---- .../ui/table/plugins/copyKeybind.plugin.ts | 10 +++---- .../table/plugins/rowSelectionModel.plugin.ts | 11 +++++--- .../browser/ui/table/plugins/tableColumn.ts | 12 ++++----- .../insights/browser/insightsDialogView.ts | 9 ++++--- .../services/restore/browser/restoreDialog.ts | 10 +++---- 10 files changed, 66 insertions(+), 59 deletions(-) diff --git a/src/sql/base/browser/dom.ts b/src/sql/base/browser/dom.ts index 6b39b0e615..365aee8d81 100644 --- a/src/sql/base/browser/dom.ts +++ b/src/sql/base/browser/dom.ts @@ -118,3 +118,10 @@ export function trapKeyboardNavigation(container: HTMLElement): IDisposable { } }); } + +/** + * Convert the SlickGrid's keydown event to VSCode standard keyboard event. + */ +export function convertJQueryKeyDownEvent(e: DOMEvent): StandardKeyboardEvent { + return new StandardKeyboardEvent((e as JQuery.KeyDownEvent).originalEvent); +} diff --git a/src/sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin.ts b/src/sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin.ts index 556fe8a679..9c5eacfcea 100644 --- a/src/sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin.ts +++ b/src/sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { convertJQueryKeyDownEvent } from 'sql/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -15,40 +16,39 @@ export class AdditionalKeyBindings implements Slick.Plugin { public init(grid: Slick.Grid) { this.grid = grid; - this.handler.subscribe(this.grid.onKeyDown, (e: DOMEvent, args) => this.handleKeyDown(e as KeyboardEvent, args)); + this.handler.subscribe(this.grid.onKeyDown, (e: DOMEvent, args) => this.handleKeyDown(convertJQueryKeyDownEvent(e), args)); } public destroy() { this.handler.unsubscribeAll(); } - private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs): void { - let event = new StandardKeyboardEvent(e); + private handleKeyDown(e: StandardKeyboardEvent, args: Slick.OnKeyDownEventArgs): void { let handled = true; - if (event.equals(KeyCode.RightArrow | KeyMod.CtrlCmd)) { + if (e.equals(KeyCode.RightArrow | KeyMod.CtrlCmd)) { this.grid.setActiveCell(args.row, this.grid.getColumns().length - 1); - } else if (event.equals(KeyCode.LeftArrow | KeyMod.CtrlCmd)) { + } else if (e.equals(KeyCode.LeftArrow | KeyMod.CtrlCmd)) { // account for row column if (this.grid.canCellBeActive(args.row, 0)) { this.grid.setActiveCell(args.row, 0); } else { this.grid.setActiveCell(args.row, 1); } - } else if (event.equals(KeyCode.UpArrow | KeyMod.CtrlCmd)) { + } else if (e.equals(KeyCode.UpArrow | KeyMod.CtrlCmd)) { this.grid.setActiveCell(0, args.cell); - } else if (event.equals(KeyCode.DownArrow | KeyMod.CtrlCmd)) { + } else if (e.equals(KeyCode.DownArrow | KeyMod.CtrlCmd)) { this.grid.setActiveCell(this.grid.getDataLength() - 1, args.cell); - } else if (event.equals(KeyCode.Home | KeyMod.CtrlCmd)) { + } else if (e.equals(KeyCode.Home | KeyMod.CtrlCmd)) { // account for row column if (this.grid.canCellBeActive(0, 0)) { this.grid.setActiveCell(0, 0); } else { this.grid.setActiveCell(0, 1); } - } else if (event.equals(KeyCode.End | KeyMod.CtrlCmd)) { + } else if (e.equals(KeyCode.End | KeyMod.CtrlCmd)) { this.grid.setActiveCell(this.grid.getDataLength() - 1, this.grid.getColumns().length - 1); - } else if (event.equals(KeyCode.KeyA | KeyMod.CtrlCmd)) { + } else if (e.equals(KeyCode.KeyA | KeyMod.CtrlCmd)) { // check if we can set the rows directly on the selectionModel, its cleaner let selectionModel = this.grid.getSelectionModel(); if (selectionModel) { @@ -61,7 +61,7 @@ export class AdditionalKeyBindings implements Slick.Plugin { if (handled) { e.preventDefault(); e.stopPropagation(); - e.stopImmediatePropagation(); + e.browserEvent.stopImmediatePropagation(); } } diff --git a/src/sql/base/browser/ui/table/plugins/cellSelectionModel.plugin.ts b/src/sql/base/browser/ui/table/plugins/cellSelectionModel.plugin.ts index d58791e181..6c13304218 100644 --- a/src/sql/base/browser/ui/table/plugins/cellSelectionModel.plugin.ts +++ b/src/sql/base/browser/ui/table/plugins/cellSelectionModel.plugin.ts @@ -6,6 +6,9 @@ import { isUndefinedOrNull } from 'vs/base/common/types'; import * as platform from 'vs/base/common/platform'; import { CellRangeSelector, ICellRangeSelector } from 'sql/base/browser/ui/table/plugins/cellRangeSelector'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { convertJQueryKeyDownEvent } from 'sql/base/browser/dom'; export interface ICellSelectionModelOptions { cellRangeSelector?: any; @@ -37,14 +40,14 @@ export class CellSelectionModel implements Slick.SelectionModel) { this.grid = grid; - this._handler.subscribe(this.grid.onKeyDown, (e: DOMEvent) => this.handleKeyDown(e as KeyboardEvent)); + this._handler.subscribe(this.grid.onKeyDown, (e: DOMEvent) => this.handleKeyDown(convertJQueryKeyDownEvent(e))); this._handler.subscribe(this.grid.onAfterKeyboardNavigation, (e: Event) => this.handleAfterKeyboardNavigationEvent()); this._handler.subscribe(this.grid.onClick, (e: DOMEvent, args: Slick.OnClickEventArgs) => this.handleCellClick(e as MouseEvent, args)); this._handler.subscribe(this.grid.onHeaderClick, (e: DOMEvent, args: Slick.OnHeaderClickEventArgs) => this.handleHeaderClick(e as MouseEvent, args)); @@ -273,19 +276,12 @@ export class CellSelectionModel implements Slick.SelectionModel implements Slick.SelectionModel implements Slick.Plugin): void { this._grid = grid; this._handler.subscribe(grid.onClick, (e: DOMEvent, args: Slick.OnClickEventArgs) => this.handleClick(args)); - this._handler.subscribe(grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs) => this.handleKeyboardEvent(e as KeyboardEvent, args)); + this._handler.subscribe(grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs) => this.handleKeyboardEvent(convertJQueryKeyDownEvent(e), args)); this._handler.subscribe(grid.onActiveCellChanged, (e: DOMEvent, args: Slick.OnActiveCellChangedEventArgs) => { this.handleActiveCellChanged(args); }); } @@ -82,9 +83,8 @@ export class CheckBoxColumn implements Slick.Plugin): void { - let event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Space) && this.isCurrentColumn(args.cell)) { + private handleKeyboardEvent(e: StandardKeyboardEvent, args: Slick.OnKeyDownEventArgs): void { + if (e.equals(KeyCode.Space) && this.isCurrentColumn(args.cell)) { this.fireOnChangeEvent(); } } diff --git a/src/sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin.ts b/src/sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin.ts index ea1fe011b0..c9790b759e 100644 --- a/src/sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin.ts +++ b/src/sql/base/browser/ui/table/plugins/checkboxSelectColumn.plugin.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { convertJQueryKeyDownEvent } from 'sql/base/browser/dom'; import { ICheckboxStyles } from 'sql/base/browser/ui/checkbox/checkbox'; import { mixin } from 'sql/base/common/objects'; import { escape } from 'sql/base/common/strings'; @@ -102,7 +103,7 @@ export class CheckboxSelectColumn implements Slick.Pl this._grid = grid; this._handler .subscribe(this._grid.onClick, (e: Event, args: Slick.OnClickEventArgs) => this.handleClick(e, args)) - .subscribe(this._grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs) => this.handleKeyDown(e as KeyboardEvent, args)) + .subscribe(this._grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs) => this.handleKeyDown(convertJQueryKeyDownEvent(e), args)) .subscribe(this._grid.onHeaderCellRendered, (e: Event, args: Slick.OnHeaderCellRenderedEventArgs) => this.handleHeaderCellRendered(e, args)) .subscribe(grid.onActiveCellChanged, (e: DOMEvent, args: Slick.OnActiveCellChangedEventArgs) => { this.handleActiveCellChanged(args); }); if (this.isCheckAllHeaderCheckboxShown()) { @@ -120,16 +121,15 @@ export class CheckboxSelectColumn implements Slick.Pl e.preventDefault(); } - private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs): void { - const event = new StandardKeyboardEvent(e); + private handleKeyDown(e: StandardKeyboardEvent, args: Slick.OnKeyDownEventArgs): void { if (args.cell !== this.index) { return; } - if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { + if (e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) { this.toggleCellCheckbox(args.row); e.stopPropagation(); - e.stopImmediatePropagation(); e.preventDefault(); + e.browserEvent.stopImmediatePropagation(); } } diff --git a/src/sql/base/browser/ui/table/plugins/copyKeybind.plugin.ts b/src/sql/base/browser/ui/table/plugins/copyKeybind.plugin.ts index 644ef29806..21b1399223 100644 --- a/src/sql/base/browser/ui/table/plugins/copyKeybind.plugin.ts +++ b/src/sql/base/browser/ui/table/plugins/copyKeybind.plugin.ts @@ -7,6 +7,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Emitter, Event } from 'vs/base/common/event'; import { isUndefinedOrNull } from 'vs/base/common/types'; +import { convertJQueryKeyDownEvent } from 'sql/base/browser/dom'; /** * Implements the various additional navigation keybindings we want out of slickgrid @@ -20,18 +21,17 @@ export class CopyKeybind implements Slick.Plugin { public init(grid: Slick.Grid) { this.grid = grid; - this.handler.subscribe(this.grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs) => this.handleKeyDown(e as KeyboardEvent, args)); + this.handler.subscribe(this.grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs) => this.handleKeyDown(convertJQueryKeyDownEvent(e), args)); } public destroy() { this.handler.unsubscribeAll(); } - private handleKeyDown(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs): void { - let event = new StandardKeyboardEvent(e); + private handleKeyDown(e: StandardKeyboardEvent, args: Slick.OnKeyDownEventArgs): void { let handled = false; - if (event.equals(KeyCode.KeyC | KeyMod.CtrlCmd)) { + if (e.equals(KeyCode.KeyC | KeyMod.CtrlCmd)) { handled = true; let selectionModel = this.grid.getSelectionModel(); let ranges: Slick.Range[]; @@ -53,7 +53,7 @@ export class CopyKeybind implements Slick.Plugin { if (handled) { e.preventDefault(); e.stopPropagation(); - e.stopImmediatePropagation(); + e.browserEvent.stopImmediatePropagation } } diff --git a/src/sql/base/browser/ui/table/plugins/rowSelectionModel.plugin.ts b/src/sql/base/browser/ui/table/plugins/rowSelectionModel.plugin.ts index fa92a82c57..68bc397326 100644 --- a/src/sql/base/browser/ui/table/plugins/rowSelectionModel.plugin.ts +++ b/src/sql/base/browser/ui/table/plugins/rowSelectionModel.plugin.ts @@ -1,5 +1,8 @@ // Adopted and converted to typescript from https://github.com/6pac/SlickGrid/blob/master/plugins/slick.rowselectionmodel.js // heavily modified +import { convertJQueryKeyDownEvent } from 'sql/base/browser/dom'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { mixin } from 'vs/base/common/objects'; const defaultOptions: IRowSelectionModelOptions = { @@ -26,7 +29,7 @@ export class RowSelectionModel implements Slick.Selec this._grid = grid; this._handler .subscribe(this._grid.onActiveCellChanged, (e: Event, data: Slick.OnActiveCellChangedEventArgs) => this.handleActiveCellChange(e, data)) - .subscribe(this._grid.onKeyDown, (e: DOMEvent) => this.handleKeyDown(e as KeyboardEvent)) + .subscribe(this._grid.onKeyDown, (e: DOMEvent) => this.handleKeyDown(convertJQueryKeyDownEvent(e))) .subscribe(this._grid.onClick, (e: DOMEvent) => this.handleClick(e as MouseEvent)); } @@ -85,9 +88,9 @@ export class RowSelectionModel implements Slick.Selec } } - private handleKeyDown(e: KeyboardEvent): void { + private handleKeyDown(e: StandardKeyboardEvent): void { const activeRow = this._grid.getActiveCell(); - if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which === 38 || e.which === 40)) { + if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.keyCode === KeyCode.UpArrow || e.keyCode === KeyCode.DownArrow)) { let selectedRows = this.getSelectedRows(); selectedRows.sort((x, y) => x - y); @@ -99,7 +102,7 @@ export class RowSelectionModel implements Slick.Selec let bottom = selectedRows[selectedRows.length - 1]; let active; - if (e.which === 40) { + if (e.keyCode === KeyCode.DownArrow) { active = activeRow.row < bottom || top === bottom ? ++bottom : ++top; } else { active = activeRow.row < bottom ? --bottom : --top; diff --git a/src/sql/base/browser/ui/table/plugins/tableColumn.ts b/src/sql/base/browser/ui/table/plugins/tableColumn.ts index b393d202e2..63acd931b6 100644 --- a/src/sql/base/browser/ui/table/plugins/tableColumn.ts +++ b/src/sql/base/browser/ui/table/plugins/tableColumn.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { convertJQueryKeyDownEvent } from 'sql/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Emitter } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -36,7 +37,7 @@ export abstract class BaseClickableColumn implements public init(grid: Slick.Grid): void { this._grid = grid; this._handler.subscribe(grid.onClick, (e: DOMEvent, args: Slick.OnClickEventArgs) => this.handleClick(args)); - this._handler.subscribe(grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs) => this.handleKeyboardEvent(e as KeyboardEvent, args)); + this._handler.subscribe(grid.onKeyDown, (e: DOMEvent, args: Slick.OnKeyDownEventArgs) => this.handleKeyboardEvent(convertJQueryKeyDownEvent(e), args)); this._handler.subscribe(grid.onActiveCellChanged, (e: DOMEvent, args: Slick.OnActiveCellChangedEventArgs) => { this.handleActiveCellChanged(args); }); } @@ -74,11 +75,10 @@ export abstract class BaseClickableColumn implements } } - private handleKeyboardEvent(e: KeyboardEvent, args: Slick.OnKeyDownEventArgs): void { - let event = new StandardKeyboardEvent(e); - if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) && this.isCellEnabled(args.row, args.cell)) { - event.stopPropagation(); - event.preventDefault(); + private handleKeyboardEvent(e: StandardKeyboardEvent, args: Slick.OnKeyDownEventArgs): void { + if ((e.equals(KeyCode.Enter) || e.equals(KeyCode.Space)) && this.isCellEnabled(args.row, args.cell)) { + e.stopPropagation(); + e.preventDefault(); this.fireClickEvent(); } } diff --git a/src/sql/workbench/services/insights/browser/insightsDialogView.ts b/src/sql/workbench/services/insights/browser/insightsDialogView.ts index 3f0cf09ca7..95f586b424 100644 --- a/src/sql/workbench/services/insights/browser/insightsDialogView.ts +++ b/src/sql/workbench/services/insights/browser/insightsDialogView.ts @@ -53,6 +53,7 @@ import { IAccessibilityService } from 'vs/platform/accessibility/common/accessib import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IComponentContextService } from 'sql/workbench/services/componentContext/browser/componentContextService'; import { defaultTableStyles } from 'sql/platform/theme/browser/defaultStyles'; +import { convertJQueryKeyDownEvent } from 'sql/base/browser/dom'; const labelDisplay = nls.localize("insights.item", "Item"); const valueDisplay = nls.localize("insights.value", "Value"); @@ -305,8 +306,8 @@ export class InsightsDialogView extends Modal { this._splitView.addView(topTableView, Sizing.Distribute); this._splitView.addView(bottomTableView, Sizing.Distribute); - this._topTable.grid.onKeyDown.subscribe(e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + this._topTable.grid.onKeyDown.subscribe((e: DOMEvent) => { + const event = convertJQueryKeyDownEvent(e); if (event.equals(KeyMod.Shift | KeyCode.Tab)) { topTableView.focus(); e.stopImmediatePropagation(); @@ -316,8 +317,8 @@ export class InsightsDialogView extends Modal { } }); - this._bottomTable.grid.onKeyDown.subscribe(e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + this._bottomTable.grid.onKeyDown.subscribe((e: DOMEvent) => { + const event = convertJQueryKeyDownEvent(e); if (event.equals(KeyMod.Shift | KeyCode.Tab)) { bottomTableView.focus(); e.stopImmediatePropagation(); diff --git a/src/sql/workbench/services/restore/browser/restoreDialog.ts b/src/sql/workbench/services/restore/browser/restoreDialog.ts index 94a3409300..64f6506229 100644 --- a/src/sql/workbench/services/restore/browser/restoreDialog.ts +++ b/src/sql/workbench/services/restore/browser/restoreDialog.ts @@ -5,7 +5,6 @@ import 'vs/css!./media/restoreDialog'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event, Emitter } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Widget } from 'vs/base/browser/ui/widget'; @@ -51,6 +50,7 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IComponentContextService } from 'sql/workbench/services/componentContext/browser/componentContextService'; import { defaultButtonStyles, defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { defaultCheckboxStyles, defaultEditableDropdownStyles, defaultSelectBoxStyles, defaultTableStyles } from 'sql/platform/theme/browser/defaultStyles'; +import { convertJQueryKeyDownEvent } from 'sql/base/browser/dom'; interface FileListElement { logicalFileName: string; @@ -450,8 +450,8 @@ export class RestoreDialog extends Modal { } })); - this._restorePlanTable.grid.onKeyDown.subscribe(e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + this._restorePlanTable.grid.onKeyDown.subscribe((e: DOMEvent) => { + const event = convertJQueryKeyDownEvent(e); if (event.equals(KeyMod.Shift | KeyCode.Tab)) { this._destinationRestoreToInputBox!.isEnabled() ? this._destinationRestoreToInputBox!.focus() : this._databaseDropdown!.focus(); e.stopImmediatePropagation(); @@ -461,8 +461,8 @@ export class RestoreDialog extends Modal { } }); - this._fileListTable.grid.onKeyDown.subscribe(e => { - let event = new StandardKeyboardEvent(e as KeyboardEvent); + this._fileListTable.grid.onKeyDown.subscribe((e: DOMEvent) => { + const event = convertJQueryKeyDownEvent(e); if (event.equals(KeyMod.Shift | KeyCode.Tab)) { if ((this._optionsMap[this._relocatedLogFileFolderOption]).isEnabled()) { (this._optionsMap[this._relocatedLogFileFolderOption]).focus();