custom scrollbar for tab container

This commit is contained in:
Benjamin Pasero 2016-06-13 11:48:31 +02:00
parent a9f9451165
commit 317df5a6af
5 changed files with 71 additions and 33 deletions

View file

@ -244,6 +244,11 @@ export class ScrollableElement extends Widget {
deltaX = e.deltaY;
}
if (this._options.scrollYToX && !deltaX) {
deltaX = e.deltaY;
deltaY = 0;
}
if (Platform.isMacintosh) {
// Give preference to vertical scrolling
if (deltaY && Math.abs(deltaX) < 0.2) {
@ -412,6 +417,7 @@ function resolveOptions(opts: ScrollableElementCreationOptions): ScrollableEleme
useShadows: (typeof opts.useShadows !== 'undefined' ? opts.useShadows : true),
handleMouseWheel: (typeof opts.handleMouseWheel !== 'undefined' ? opts.handleMouseWheel : true),
flipAxes: (typeof opts.flipAxes !== 'undefined' ? opts.flipAxes : false),
scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false),
mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1),
arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11),

View file

@ -36,9 +36,14 @@ export interface ScrollableElementCreationOptions {
handleMouseWheel?: boolean;
/**
* Flip axes. Treat vertical scrolling like horizontal and vice-versa.
* Defaults to false;
* Defaults to false.
*/
flipAxes?: boolean;
/**
* If enabled, will scroll horizontally when scrolling vertical.
* Defaults to false.
*/
scrollYToX?: boolean;
/**
* A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.
* Defaults to 1.
@ -115,6 +120,7 @@ export interface ScrollableElementResolvedOptions {
useShadows: boolean;
handleMouseWheel: boolean;
flipAxes: boolean;
scrollYToX: boolean;
mouseWheelScrollSensitivity: number;
arrowSize: number;
listenOnDomNode: HTMLElement;

View file

@ -5,8 +5,11 @@
/* Tabs Container */
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container {
.monaco-workbench > .part.editor > .content > .one-editor-container > .title > .monaco-scrollable-element {
flex: 1;
}
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container {
display: flex;
overflow: scroll;
background-color: rgba(128, 128, 128, 0.2);
@ -67,27 +70,27 @@
/* Tab Close */
.monaco-workbench > .part.editor > .content > .one-editor-container .title > .tabs-container > .tab > .tab-close {
.monaco-workbench > .part.editor > .content > .one-editor-container .title .tabs-container > .tab > .tab-close {
opacity: 0; /* hide the close button by default */
}
.monaco-workbench > .part.editor > .content > .one-editor-container .title.active > .tabs-container > .tab.active > .tab-close, /* always show it for active tab */
.monaco-workbench > .part.editor > .content > .one-editor-container .title.active > .tabs-container > .tab:hover > .tab-close, /* always show it on hover */
.monaco-workbench > .part.editor > .content > .one-editor-container .title.active > .tabs-container > .tab.active:hover > .tab-close, /* always show it on hover */
.monaco-workbench > .part.editor > .content > .one-editor-container .title > .tabs-container > .tab.dirty > .tab-close { /* always show it for dirty tabs */
.monaco-workbench > .part.editor > .content > .one-editor-container .title.active .tabs-container > .tab.active > .tab-close, /* always show it for active tab */
.monaco-workbench > .part.editor > .content > .one-editor-container .title.active .tabs-container > .tab:hover > .tab-close, /* always show it on hover */
.monaco-workbench > .part.editor > .content > .one-editor-container .title.active .tabs-container > .tab.active:hover > .tab-close, /* always show it on hover */
.monaco-workbench > .part.editor > .content > .one-editor-container .title .tabs-container > .tab.dirty > .tab-close { /* always show it for dirty tabs */
opacity: 1;
}
.monaco-workbench > .part.editor > .content > .one-editor-container .title > .tabs-container > .tab.active > .tab-close, /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content > .one-editor-container .title > .tabs-container > .tab.active:hover > .tab-close { /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content > .one-editor-container .title .tabs-container > .tab.active > .tab-close, /* show dimmed for inactive group */
.monaco-workbench > .part.editor > .content > .one-editor-container .title .tabs-container > .tab.active:hover > .tab-close { /* show dimmed for inactive group */
opacity: 0.5;
}
.monaco-workbench > .part.editor > .content > .one-editor-container .title > .tabs-container > .tab:hover > .tab-close { /* show more dimmed for inactive group and tab */
.monaco-workbench > .part.editor > .content > .one-editor-container .title .tabs-container > .tab:hover > .tab-close { /* show more dimmed for inactive group and tab */
opacity: 0.4;
}
.monaco-workbench > .part.editor > .content > .one-editor-container .title > .tabs-container > .tab > .tab-close .action-label {
.monaco-workbench > .part.editor > .content > .one-editor-container .title .tabs-container > .tab > .tab-close .action-label {
width: 20px;
}

View file

@ -6,7 +6,7 @@
/* Editor Label */
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .title-label,
.monaco-workbench > .part.editor > .content > .one-editor-container > .title > .tabs-container > .tab .tab-label {
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab .tab-label {
line-height: 35px;
white-space: nowrap;
flex: 1;
@ -14,34 +14,34 @@
}
.monaco-workbench > .part.editor > .content > .one-editor-container > .title.pinned .title-label,
.monaco-workbench > .part.editor > .content > .one-editor-container > .title > .tabs-container > .tab.pinned .tab-label {
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab.pinned .tab-label {
font-style: normal;
}
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .title-label a,
.monaco-workbench > .part.editor > .content > .one-editor-container > .title > .tabs-container > .tab .tab-label a {
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab .tab-label a {
text-decoration: none;
font-size: 13px;
cursor: pointer;
}
.vs .monaco-workbench > .part.editor > .content > .one-editor-container > .title .title-label a,
.vs .monaco-workbench > .part.editor > .content > .one-editor-container > .title > .tabs-container > .tab .tab-label a {
.vs .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab .tab-label a {
color: rgba(51, 51, 51, 0.5);
}
.vs .monaco-workbench > .part.editor > .content > .one-editor-container .title.active .title-label a,
.vs .monaco-workbench > .part.editor > .content > .one-editor-container .title.active > .tabs-container > .tab .tab-label a {
.vs .monaco-workbench > .part.editor > .content > .one-editor-container .title.active .tabs-container > .tab .tab-label a {
color: #333333;
}
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-container > .title .title-label a,
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-container > .title > .tabs-container > .tab .tab-label a {
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-container > .title .tabs-container > .tab .tab-label a {
color: rgba(255, 255, 255, 0.5);
}
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-container .title.active .title-label a,
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-container .title.active > .tabs-container > .tab .tab-label a {
.vs-dark .monaco-workbench > .part.editor > .content > .one-editor-container .title.active .tabs-container > .tab .tab-label a {
color: white;
}
@ -49,7 +49,7 @@
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .title-actions .action-label,
.monaco-workbench > .part.editor > .content > .one-editor-container > .title .group-actions .action-label,
.monaco-workbench > .part.editor > .content > .one-editor-container .title > .tabs-container > .tab > .tab-close .action-label {
.monaco-workbench > .part.editor > .content > .one-editor-container .title .tabs-container > .tab > .tab-close .action-label {
display: block;
height: 35px;
width: 28px;

View file

@ -28,6 +28,8 @@ import {IInstantiationService} from 'vs/platform/instantiation/common/instantiat
import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService';
import {TitleControl} from 'vs/workbench/browser/parts/editor/titleControl';
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
import {ScrollableElement} from 'vs/base/browser/ui/scrollbar/scrollableElement';
import {ScrollbarVisibility} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
export class TabsTitleControl extends TitleControl {
@ -36,6 +38,7 @@ export class TabsTitleControl extends TitleControl {
private titleContainer: HTMLElement;
private tabsContainer: HTMLElement;
private activeTab: HTMLElement;
private scrollbar: ScrollableElement;
private groupActionsToolbar: ToolBar;
private tabDisposeables: IDisposable[];
@ -72,7 +75,23 @@ export class TabsTitleControl extends TitleControl {
// Tabs Container
this.tabsContainer = document.createElement('div');
DOM.addClass(this.tabsContainer, 'tabs-container');
this.titleContainer.appendChild(this.tabsContainer);
// Custom Scrollbar
this.scrollbar = new ScrollableElement(this.tabsContainer, {
horizontal: ScrollbarVisibility.Auto,
vertical: ScrollbarVisibility.Hidden,
scrollYToX: true,
useShadows: false,
canUseTranslate3d: true,
horizontalScrollbarSize: 3
});
this.tabsContainer.style.overflow = 'scroll'; // custom scrollbar is eager on removing this style but we want it for DND scroll feedback
this.scrollbar.onScroll(e => {
this.tabsContainer.scrollLeft = e.scrollLeft;
});
this.titleContainer.appendChild(this.scrollbar.getDomNode());
// Drag over
this.toDispose.push(DOM.addDisposableListener(this.tabsContainer, DOM.EventType.DRAG_OVER, (e: DragEvent) => {
@ -113,14 +132,6 @@ export class TabsTitleControl extends TitleControl {
}
}));
// Convert mouse wheel vertical scroll to horizontal
this.toDispose.push(DOM.addDisposableListener(this.tabsContainer, DOM.EventType.WHEEL, (e: WheelEvent) => {
if (e.deltaY && !e.deltaX) {
DOM.EventHelper.stop(e);
this.tabsContainer.scrollLeft += e.deltaY;
}
}));
// Group Actions
const groupActionsContainer = document.createElement('div');
DOM.addClass(groupActionsContainer, 'group-actions');
@ -269,24 +280,36 @@ export class TabsTitleControl extends TitleControl {
return;
}
const visibleContainerWidth = this.tabsContainer.offsetWidth;
const totalContainerWidth = this.tabsContainer.scrollWidth;
// Update scrollbar
this.scrollbar.updateState({
width: visibleContainerWidth,
scrollWidth: totalContainerWidth
});
// Always reveal the active one
const containerWidth = this.tabsContainer.offsetWidth;
const containerScrollPosX = this.tabsContainer.scrollLeft;
const activeTabPosX = this.activeTab.offsetLeft;
const activeTabWidth = this.activeTab.offsetWidth;
// Tab is overflowing to the right: Scroll minimally until the element is fully visible to the right
if (containerScrollPosX + containerWidth < activeTabPosX + activeTabWidth) {
this.tabsContainer.scrollLeft += ((activeTabPosX + activeTabWidth) /* right corner of tab */ - (containerScrollPosX + containerWidth) /* right corner of view port */);
if (containerScrollPosX + visibleContainerWidth < activeTabPosX + activeTabWidth) {
this.scrollbar.updateState({
scrollLeft: containerScrollPosX + ((activeTabPosX + activeTabWidth) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */)
});
}
// Tab is overlflowng to the left: Scroll it into view to the left
else if (containerScrollPosX > activeTabPosX) {
this.tabsContainer.scrollLeft = this.activeTab.offsetLeft;
this.scrollbar.updateState({
scrollLeft: this.activeTab.offsetLeft
});
}
// Update enablement of certain actions that depend on overflow
const isOverflowing = (this.tabsContainer.scrollWidth > containerWidth);
const isOverflowing = (totalContainerWidth > visibleContainerWidth);
this.showEditorsOfLeftGroup.enabled = isOverflowing;
this.showEditorsOfCenterGroup.enabled = isOverflowing;
this.showEditorsOfRightGroup.enabled = isOverflowing;