From 4967ca4580ef25921cc34c169560838fb4363e11 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 28 Nov 2018 10:55:50 +0100 Subject: [PATCH] list: emit mouse events on root fixes #63598 --- src/vs/base/browser/ui/list/list.ts | 8 ++--- src/vs/base/browser/ui/list/listPaging.ts | 2 +- src/vs/base/browser/ui/list/listView.ts | 34 +++++++++---------- src/vs/base/browser/ui/list/listWidget.ts | 11 ++++-- .../browser/ui/selectBox/selectBoxCustom.ts | 4 +-- src/vs/base/browser/ui/tree/dataTree.ts | 9 +++-- .../parts/notifications/notificationsList.ts | 4 +++ .../parts/debug/browser/breakpointsView.ts | 4 +++ .../runtimeExtensionsEditor.ts | 4 +++ .../electron-browser/views/openEditorsView.ts | 4 +++ .../preferences/browser/keybindingsEditor.ts | 4 +++ .../parts/scm/electron-browser/scmViewlet.ts | 9 ++++- 12 files changed, 67 insertions(+), 30 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 1af9112d491..c3e4c0d2471 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -28,24 +28,24 @@ export interface IListEvent { export interface IListMouseEvent { browserEvent: MouseEvent; element: T | undefined; - index: number; + index: number | undefined; } export interface IListTouchEvent { browserEvent: TouchEvent; element: T | undefined; - index: number; + index: number | undefined; } export interface IListGestureEvent { browserEvent: GestureEvent; element: T | undefined; - index: number; + index: number | undefined; } export interface IListContextMenuEvent { browserEvent: UIEvent; element: T | undefined; - index: number; + index: number | undefined; anchor: HTMLElement | { x: number; y: number; } | undefined; } diff --git a/src/vs/base/browser/ui/list/listPaging.ts b/src/vs/base/browser/ui/list/listPaging.ts index d4491cecefd..b218825a865 100644 --- a/src/vs/base/browser/ui/list/listPaging.ts +++ b/src/vs/base/browser/ui/list/listPaging.ts @@ -134,7 +134,7 @@ export class PagedList implements IDisposable { } get onContextMenu(): Event> { - return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => ({ element: this._model.get(element!), index, anchor, browserEvent })); + return mapEvent(this.list.onContextMenu, ({ element, index, anchor, browserEvent }) => (typeof element === 'undefined' ? { element, index, anchor, browserEvent } : { element: this._model.get(element), index, anchor, browserEvent })); } get model(): IPagedModel { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index a6bc014b9e6..97bf88a198d 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -392,35 +392,35 @@ export class ListView implements ISpliceable, IDisposable { // Events - @memoize get onMouseClick(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseDblClick(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseMiddleClick(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'auxclick'), e => this.toMouseEvent(e as MouseEvent)), e => e.index >= 0 && e.browserEvent.button === 1); } - @memoize get onMouseUp(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseDown(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseOver(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseMove(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onMouseOut(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onContextMenu(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)), e => e.index >= 0); } - @memoize get onTouchStart(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)), e => e.index >= 0); } - @memoize get onTap(): Event> { return filterEvent(mapEvent(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)), e => e.index >= 0); } + @memoize get onMouseClick(): Event> { return mapEvent(domEvent(this.domNode, 'click'), e => this.toMouseEvent(e)); } + @memoize get onMouseDblClick(): Event> { return mapEvent(domEvent(this.domNode, 'dblclick'), e => this.toMouseEvent(e)); } + @memoize get onMouseMiddleClick(): Event> { return filterEvent(mapEvent(domEvent(this.domNode, 'auxclick'), e => this.toMouseEvent(e as MouseEvent)), e => e.browserEvent.button === 1); } + @memoize get onMouseUp(): Event> { return mapEvent(domEvent(this.domNode, 'mouseup'), e => this.toMouseEvent(e)); } + @memoize get onMouseDown(): Event> { return mapEvent(domEvent(this.domNode, 'mousedown'), e => this.toMouseEvent(e)); } + @memoize get onMouseOver(): Event> { return mapEvent(domEvent(this.domNode, 'mouseover'), e => this.toMouseEvent(e)); } + @memoize get onMouseMove(): Event> { return mapEvent(domEvent(this.domNode, 'mousemove'), e => this.toMouseEvent(e)); } + @memoize get onMouseOut(): Event> { return mapEvent(domEvent(this.domNode, 'mouseout'), e => this.toMouseEvent(e)); } + @memoize get onContextMenu(): Event> { return mapEvent(domEvent(this.domNode, 'contextmenu'), e => this.toMouseEvent(e)); } + @memoize get onTouchStart(): Event> { return mapEvent(domEvent(this.domNode, 'touchstart'), e => this.toTouchEvent(e)); } + @memoize get onTap(): Event> { return mapEvent(domEvent(this.rowsContainer, TouchEventType.Tap), e => this.toGestureEvent(e)); } private toMouseEvent(browserEvent: MouseEvent): IListMouseEvent { const index = this.getItemIndexFromEventTarget(browserEvent.target || null); - const item = index < 0 ? undefined : this.items[index]; + const item = typeof index === 'undefined' ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private toTouchEvent(browserEvent: TouchEvent): IListTouchEvent { const index = this.getItemIndexFromEventTarget(browserEvent.target || null); - const item = index < 0 ? undefined : this.items[index]; + const item = typeof index === 'undefined' ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } private toGestureEvent(browserEvent: GestureEvent): IListGestureEvent { const index = this.getItemIndexFromEventTarget(browserEvent.initialTarget || null); - const item = index < 0 ? undefined : this.items[index]; + const item = typeof index === 'undefined' ? undefined : this.items[index]; const element = item && item.element; return { browserEvent, index, element }; } @@ -433,7 +433,7 @@ export class ListView implements ISpliceable, IDisposable { this.rerender(e.scrollTop, e.height); } } catch (err) { - console.log('Got bad scroll event:', e); + console.error('Got bad scroll event:', e); throw err; } } @@ -499,7 +499,7 @@ export class ListView implements ISpliceable, IDisposable { // Util - private getItemIndexFromEventTarget(target: EventTarget | null): number { + private getItemIndexFromEventTarget(target: EventTarget | null): number | undefined { let element: HTMLElement | null = target as (HTMLElement | null); while (element instanceof HTMLElement && element !== this.rowsContainer) { @@ -516,7 +516,7 @@ export class ListView implements ISpliceable, IDisposable { element = element.parentElement; } - return -1; + return undefined; } private getRenderRange(renderTop: number, renderHeight: number): IRange { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 9a0457d48a6..7b5a9131b18 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -508,11 +508,18 @@ class MouseController implements IDisposable { const selection = this.list.getSelection(); reference = reference === undefined ? selection[0] : reference; + const focus = e.index; + + if (typeof focus === 'undefined') { + this.list.setFocus([], e.browserEvent); + this.list.setSelection([], e.browserEvent); + return; + } + if (this.multipleSelectionSupport && this.isSelectionRangeChangeEvent(e)) { return this.changeSelection(e, reference); } - const focus = e.index; if (selection.every(s => s !== focus)) { this.list.setFocus([focus], e.browserEvent); } @@ -556,7 +563,7 @@ class MouseController implements IDisposable { } private changeSelection(e: IListMouseEvent | IListTouchEvent, reference: number | undefined): void { - const focus = e.index; + const focus = e.index!; if (this.isSelectionRangeChangeEvent(e) && reference !== undefined) { const min = Math.min(reference, focus); diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 26c4ba02038..14504ca6c2a 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -780,8 +780,8 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate this.onMouseUp(e), this, this.toDispose); this.toDispose.push( - this.selectList.onDidBlur(e => this.onListBlur()), - this.selectList.onMouseOver(e => this.selectList.setFocus([e.index])), + this.selectList.onDidBlur(_ => this.onListBlur()), + this.selectList.onMouseOver(e => typeof e.index !== 'undefined' && this.selectList.setFocus([e.index])), this.selectList.onFocusChange(e => this.onListFocus(e)) ); diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 6f4c341860e..1b612f43be0 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -289,15 +289,18 @@ export class DataTree, TFilterData = void> implements // Tree navigation getParentElement(element: T): T | null { - return this.tree.getParentElement(this.getNode(element)).element; + const node = this.tree.getParentElement(this.getNode(element)); + return node && node.element; } getFirstElementChild(element: T | null = null): T | null { - return this.tree.getFirstElementChild(this.getNode(element)).element; + const node = this.tree.getFirstElementChild(this.getNode(element)); + return node && node.element; } getLastElementAncestor(element: T | null = null): T | null { - return this.tree.getLastElementAncestor(this.getNode(element)).element; + const node = this.tree.getLastElementAncestor(this.getNode(element)); + return node && node.element; } // Implementation diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index 76dc13247ad..93829a6230e 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -84,6 +84,10 @@ export class NotificationsList extends Themable { // Context menu to copy message const copyAction = this._register(this.instantiationService.createInstance(CopyNotificationMessageAction, CopyNotificationMessageAction.ID, CopyNotificationMessageAction.LABEL)); this._register((this.list.onContextMenu(e => { + if (!e.element) { + return; + } + this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => [copyAction], diff --git a/src/vs/workbench/parts/debug/browser/breakpointsView.ts b/src/vs/workbench/parts/debug/browser/breakpointsView.ts index c74d9c262f7..e4e3b8549bb 100644 --- a/src/vs/workbench/parts/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/parts/debug/browser/breakpointsView.ts @@ -134,6 +134,10 @@ export class BreakpointsView extends ViewletPanel { } private onListContextMenu(e: IListContextMenuEvent): void { + if (!e.element) { + return; + } + const actions: IAction[] = []; const element = e.element; diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index 1f66c2e71b7..4227a53a04d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -410,6 +410,10 @@ export class RuntimeExtensionsEditor extends BaseEditor { this._list.splice(0, this._list.length, this._elements); this._list.onContextMenu((e) => { + if (!e.element) { + return; + } + const actions: IAction[] = []; actions.push(new ReportExtensionIssueAction(e.element)); diff --git a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts index 04a2c81e9d7..a7dbaa9fe91 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -385,6 +385,10 @@ export class OpenEditorsView extends ViewletPanel { } private onListContextMenu(e: IListContextMenuEvent): void { + if (!e.element) { + return; + } + const element = e.element; this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index c06fb76f736..4a24205d7e1 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -552,6 +552,10 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor } private onContextMenu(e: IListContextMenuEvent): void { + if (!e.element) { + return; + } + if (e.element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) { this.selectEntry(e.element); this.contextMenuService.showContextMenu({ diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 2a9ad503f2a..3f3b422a84f 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -316,8 +316,11 @@ class MainPanel extends ViewletPanel { } private onListContextMenu(e: IListContextMenuEvent): void { - const repository = e.element; + if (!e.element) { + return; + } + const repository = e.element; const contextKeyService = this.contextKeyService.createScoped(); const scmProviderKey = contextKeyService.createKey('scmProvider', void 0); scmProviderKey.set(repository.provider.contextValue); @@ -986,6 +989,10 @@ export class RepositoryPanel extends ViewletPanel { } private onListContextMenu(e: IListContextMenuEvent): void { + if (!e.element) { + return; + } + const element = e.element; let actions: IAction[]; -- GitLab