in src/vs/workbench/browser/parts/editor/tabsTitleControl.ts [462:673]
private registerTabListeners(tab: HTMLElement, index: number): IDisposable {
const disposables = new DisposableStore();
const handleClickOrTouch = (e: MouseEvent | GestureEvent): void => {
tab.blur();
if (e instanceof MouseEvent && e.button !== 0) {
if (e.button === 1) {
e.preventDefault(); // required to prevent auto-scrolling (https://github.com/Microsoft/vscode/issues/16690)
}
return undefined; // only for left mouse click
}
if (this.originatesFromTabActionBar(e)) {
return; // not when clicking on actions
}
// Open tabs editor
const input = this.group.getEditor(index);
if (input) {
this.group.openEditor(input);
}
return undefined;
};
const showContextMenu = (e: Event) => {
EventHelper.stop(e);
const input = this.group.getEditor(index);
if (input) {
this.onContextMenu(input, e, tab);
}
};
// Open on Click / Touch
disposables.add(addDisposableListener(tab, EventType.MOUSE_DOWN, (e: MouseEvent) => handleClickOrTouch(e)));
disposables.add(addDisposableListener(tab, TouchEventType.Tap, (e: GestureEvent) => handleClickOrTouch(e)));
// Touch Scroll Support
disposables.add(addDisposableListener(tab, TouchEventType.Change, (e: GestureEvent) => {
this.tabsScrollbar.setScrollPosition({ scrollLeft: this.tabsScrollbar.getScrollPosition().scrollLeft - e.translationX });
}));
// Close on mouse middle click
disposables.add(addDisposableListener(tab, EventType.MOUSE_UP, (e: MouseEvent) => {
EventHelper.stop(e);
tab.blur();
if (e.button === 1 /* Middle Button*/) {
e.stopPropagation(); // for https://github.com/Microsoft/vscode/issues/56715
this.blockRevealActiveTabOnce();
this.closeOneEditorAction.run({ groupId: this.group.id, editorIndex: index });
}
}));
// Context menu on Shift+F10
disposables.add(addDisposableListener(tab, EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
if (event.shiftKey && event.keyCode === KeyCode.F10) {
showContextMenu(e);
}
}));
// Context menu on touch context menu gesture
disposables.add(addDisposableListener(tab, TouchEventType.Contextmenu, (e: GestureEvent) => {
showContextMenu(e);
}));
// Keyboard accessibility
disposables.add(addDisposableListener(tab, EventType.KEY_UP, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
let handled = false;
// Run action on Enter/Space
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
handled = true;
const input = this.group.getEditor(index);
if (input) {
this.group.openEditor(input);
}
}
// Navigate in editors
else if ([KeyCode.LeftArrow, KeyCode.RightArrow, KeyCode.UpArrow, KeyCode.DownArrow, KeyCode.Home, KeyCode.End].some(kb => event.equals(kb))) {
let targetIndex: number;
if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.UpArrow)) {
targetIndex = index - 1;
} else if (event.equals(KeyCode.RightArrow) || event.equals(KeyCode.DownArrow)) {
targetIndex = index + 1;
} else if (event.equals(KeyCode.Home)) {
targetIndex = 0;
} else {
targetIndex = this.group.count - 1;
}
const target = this.group.getEditor(targetIndex);
if (target) {
handled = true;
this.group.openEditor(target, { preserveFocus: true });
(<HTMLElement>this.tabsContainer.childNodes[targetIndex]).focus();
}
}
if (handled) {
EventHelper.stop(e, true);
}
// moving in the tabs container can have an impact on scrolling position, so we need to update the custom scrollbar
this.tabsScrollbar.setScrollPosition({
scrollLeft: this.tabsContainer.scrollLeft
});
}));
// Pin on double click
disposables.add(addDisposableListener(tab, EventType.DBLCLICK, (e: MouseEvent) => {
EventHelper.stop(e);
this.group.pinEditor(this.group.getEditor(index) || undefined);
}));
// Context menu
disposables.add(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => {
EventHelper.stop(e, true);
const input = this.group.getEditor(index);
if (input) {
this.onContextMenu(input, e, tab);
}
}, true /* use capture to fix https://github.com/Microsoft/vscode/issues/19145 */));
// Drag support
disposables.add(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => {
const editor = this.group.getEditor(index);
if (!editor) {
return;
}
this.editorTransfer.setData([new DraggedEditorIdentifier({ editor, groupId: this.group.id })], DraggedEditorIdentifier.prototype);
e.dataTransfer!.effectAllowed = 'copyMove';
// Apply some datatransfer types to allow for dragging the element outside of the application
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
if (resource) {
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
}
// Fixes https://github.com/Microsoft/vscode/issues/18733
addClass(tab, 'dragged');
scheduleAtNextAnimationFrame(() => removeClass(tab, 'dragged'));
}));
// Drop support
disposables.add(new DragAndDropObserver(tab, {
onDragEnter: e => {
// Update class to signal drag operation
addClass(tab, 'dragged-over');
// Return if transfer is unsupported
if (!this.isSupportedDropTransfer(e)) {
e.dataTransfer!.dropEffect = 'none';
return;
}
// Return if dragged editor is the current tab dragged over
let isLocalDragAndDrop = false;
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
isLocalDragAndDrop = true;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) {
e.dataTransfer!.dropEffect = 'none';
return;
}
}
// Update the dropEffect to "copy" if there is no local data to be dragged because
// in that case we can only copy the data into and not move it from its source
if (!isLocalDragAndDrop) {
e.dataTransfer!.dropEffect = 'copy';
}
this.updateDropFeedback(tab, true, index);
},
onDragLeave: e => {
removeClass(tab, 'dragged-over');
this.updateDropFeedback(tab, false, index);
},
onDragEnd: e => {
removeClass(tab, 'dragged-over');
this.updateDropFeedback(tab, false, index);
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
},
onDrop: e => {
removeClass(tab, 'dragged-over');
this.updateDropFeedback(tab, false, index);
this.onDrop(e, index);
}
}));
return disposables;
}