diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 09183772b77..2552ac14916 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -822,6 +822,7 @@ export const EventType = { MOUSE_OUT: 'mouseout', MOUSE_ENTER: 'mouseenter', MOUSE_LEAVE: 'mouseleave', + MOUSE_WHEEL: browser.isEdge ? 'mousewheel' : 'wheel', POINTER_UP: 'pointerup', POINTER_DOWN: 'pointerdown', POINTER_MOVE: 'pointermove', diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index c840e64c882..85418a90e46 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/scrollbars'; -import { isEdge } from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IMouseEvent, StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; @@ -336,7 +335,7 @@ export abstract class AbstractScrollableElement extends Widget { this._onMouseWheel(new StandardWheelEvent(browserEvent)); }; - this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, isEdge ? 'mousewheel' : 'wheel', onMouseWheel, { passive: false })); + this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { passive: false })); } } diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 479fa090c6e..1e7b0929da4 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as browser from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import { StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { TimeoutTimer } from 'vs/base/common/async'; @@ -119,7 +118,7 @@ export class MouseHandler extends ViewEventHandler { e.stopPropagation(); } }; - this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, browser.isEdge ? 'mousewheel' : 'wheel', onMouseWheel, { capture: true, passive: false })); + this._register(dom.addDisposableListener(this.viewHelper.viewDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { capture: true, passive: false })); this._context.addEventHandler(this); } diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index f33c1235557..86737feb3bd 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -209,7 +209,7 @@ export class TabsTitleControl extends TitleControl { this._register(addDisposableListener(tabsContainer, EventType.SCROLL, () => { if (hasClass(tabsContainer, 'scroll')) { tabsScrollbar.setScrollPosition({ - scrollLeft: tabsContainer.scrollLeft // during DND the container gets scrolled so we need to update the custom scrollbar + scrollLeft: tabsContainer.scrollLeft // during DND the container gets scrolled so we need to update the custom scrollbar }); } })); @@ -320,6 +320,42 @@ export class TabsTitleControl extends TitleControl { } } })); + + // Mouse-wheel support to switch to tabs optionally + this._register(addDisposableListener(tabsContainer, EventType.MOUSE_WHEEL, (e: MouseWheelEvent) => { + const activeEditor = this.group.activeEditor; + if (!activeEditor || this.group.count < 2) { + return; // need at least 2 open editors + } + + // Shift-key enables or disables this behaviour depending on the setting + if (this.accessor.partOptions.scrollToSwitchTabs === 'off') { + if (!e.shiftKey) { + return; // 'off': only enable this when Shift-key is pressed + } + } else { + if (e.shiftKey) { + return; // 'on': only enable this when Shift-key is not pressed + } + } + + // Figure out scrolling direction + let scrollingUp = e.deltaX < 0 || e.deltaY < 0; + if (this.accessor.partOptions.scrollToSwitchTabs === 'reverse') { + scrollingUp = !scrollingUp; + } + + const nextEditor = this.group.getEditorByIndex(this.group.getIndexOfEditor(activeEditor) + (scrollingUp ? -1 : 1)); + if (!nextEditor) { + return; + } + + // Open it + this.group.openEditor(nextEditor); + + // Disable normal scrolling, opening the editor will already reveal it properly + EventHelper.stop(e, true); + })); } protected updateEditorActionsToolbar(): void { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 30539e9b038..c2f2d72fc0c 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -32,6 +32,17 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio 'description': nls.localize('showEditorTabs', "Controls whether opened editors should show in tabs or not."), 'default': true }, + 'workbench.editor.scrollToSwitchTabs': { + 'type': 'string', + 'enum': ['off', 'natural', 'reverse'], + 'enumDescriptions': [ + nls.localize('workbench.editor.scrollToSwitchTabs.off', "Tabs will reveal when scrolling with the mouse but not open. You can press and hold the Shift-key to switch tabs while scrolling."), + nls.localize('workbench.editor.scrollToSwitchTabs.natural', "Tabs will open when scrolling with the mouse in natural scrolling direction (scroll up to switch to the tab on the left and down for the tab on the right). You can press and hold the Shift-key to disable this behaviour for that duration."), + nls.localize('workbench.editor.scrollToSwitchTabs.reverse', "Tabs will open when scrolling with the mouse in reverse scrolling direction (scroll down to switch to the tab on the left and up for the tab on the right). You can press and hold the Shift-key to disable this behaviour for that duration."), + ], + 'default': 'off', + 'description': nls.localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'scrollToSwitchTabs' }, "Controls wether scrolling over tabs will open them or not. By default tabs will only reveal upon scrolling, but not open. You can press and hold the Shift-key while scrolling to change this behaviour for that duration.") + }, 'workbench.editor.highlightModifiedTabs': { 'type': 'boolean', 'description': nls.localize('highlightModifiedTabs', "Controls whether a top border is drawn on modified (dirty) editor tabs or not."), diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 9c6a4f57a92..3be0e28c8c7 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1303,6 +1303,7 @@ export interface IWorkbenchEditorConfiguration { interface IEditorPartConfiguration { showTabs?: boolean; + scrollToSwitchTabs?: 'off' | 'natural' | 'reverse'; highlightModifiedTabs?: boolean; tabCloseButton?: 'left' | 'right' | 'off'; tabSizing?: 'fit' | 'shrink';