提交 c790c477 编写于 作者: B Benjamin Pasero

tabs: push pixel dimensions to control

上级 b7201a6e
...@@ -2038,7 +2038,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro ...@@ -2038,7 +2038,9 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
// Layout title controls // Layout title controls
POSITIONS.forEach(position => { POSITIONS.forEach(position => {
this.getTitleAreaControl(position).layout(); const siloWidth = this.layoutVertically ? this.silosSize[position] : this.dimension.width;
this.getTitleAreaControl(position).layout(new Dimension(siloWidth, EditorGroupsControl.EDITOR_TITLE_HEIGHT));
}); });
// Update minimized state // Update minimized state
......
...@@ -44,6 +44,7 @@ import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, ...@@ -44,6 +44,7 @@ import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND,
import { activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { activeContrastBorder, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { Dimension } from 'vs/base/browser/builder';
interface IEditorInputLabel { interface IEditorInputLabel {
name: string; name: string;
...@@ -56,11 +57,14 @@ type AugmentedLabel = IEditorInputLabel & { editor: IEditorInput }; ...@@ -56,11 +57,14 @@ type AugmentedLabel = IEditorInputLabel & { editor: IEditorInput };
export class TabsTitleControl extends TitleControl { export class TabsTitleControl extends TitleControl {
private titleContainer: HTMLElement; private titleContainer: HTMLElement;
private tabsContainer: HTMLElement; private tabsContainer: HTMLElement;
private editorToolbarContainer: HTMLElement;
private activeTab: HTMLElement; private activeTab: HTMLElement;
private editorLabels: ResourceLabel[]; private editorLabels: ResourceLabel[];
private scrollbar: ScrollableElement; private scrollbar: ScrollableElement;
private tabDisposeables: IDisposable[]; private tabDisposeables: IDisposable[];
private blockRevealActiveTab: boolean; private blockRevealActiveTab: boolean;
private dimension: Dimension;
private editorToolbarWidth: number;
constructor( constructor(
@IContextMenuService contextMenuService: IContextMenuService, @IContextMenuService contextMenuService: IContextMenuService,
...@@ -83,6 +87,7 @@ export class TabsTitleControl extends TitleControl { ...@@ -83,6 +87,7 @@ export class TabsTitleControl extends TitleControl {
this.tabDisposeables = []; this.tabDisposeables = [];
this.editorLabels = []; this.editorLabels = [];
this.editorToolbarWidth = 0;
} }
protected initActions(services: IInstantiationService): void { protected initActions(services: IInstantiationService): void {
...@@ -223,13 +228,13 @@ export class TabsTitleControl extends TitleControl { ...@@ -223,13 +228,13 @@ export class TabsTitleControl extends TitleControl {
} }
})); }));
// Editor Actions Container // Editor Toolbar Container
const editorActionsContainer = document.createElement('div'); this.editorToolbarContainer = document.createElement('div');
DOM.addClass(editorActionsContainer, 'editor-actions'); DOM.addClass(this.editorToolbarContainer, 'editor-actions');
this.titleContainer.appendChild(editorActionsContainer); this.titleContainer.appendChild(this.editorToolbarContainer);
// Editor Actions Toolbar // Editor Actions Toolbar
this.createEditorActionsToolBar(editorActionsContainer); this.createEditorActionsToolBar(this.editorToolbarContainer);
} }
private updateDropFeedback(element: HTMLElement, isDND: boolean, index?: number): void { private updateDropFeedback(element: HTMLElement, isDND: boolean, index?: number): void {
...@@ -280,66 +285,64 @@ export class TabsTitleControl extends TitleControl { ...@@ -280,66 +285,64 @@ export class TabsTitleControl extends TitleControl {
// Tab label and styles // Tab label and styles
editorsOfGroup.forEach((editor, index) => { editorsOfGroup.forEach((editor, index) => {
const tabContainer = this.tabsContainer.children[index]; const tabContainer = this.tabsContainer.children[index] as HTMLElement;
if (tabContainer instanceof HTMLElement) { const isPinned = group.isPinned(index);
const isPinned = group.isPinned(index); const isTabActive = group.isActive(editor);
const isTabActive = group.isActive(editor); const isDirty = editor.isDirty();
const isDirty = editor.isDirty();
const label = labels[index];
const label = labels[index]; const name = label.name;
const name = label.name; const description = label.description || '';
const description = label.description || ''; const title = label.title || '';
const title = label.title || '';
// Container
// Container tabContainer.setAttribute('aria-label', `${name}, tab`);
tabContainer.setAttribute('aria-label', `${name}, tab`); tabContainer.title = title;
tabContainer.title = title; tabContainer.style.borderLeftColor = (index !== 0) ? (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)) : null;
tabContainer.style.borderLeftColor = (index !== 0) ? (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)) : null; tabContainer.style.borderRightColor = (index === editorsOfGroup.length - 1) ? (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)) : null;
tabContainer.style.borderRightColor = (index === editorsOfGroup.length - 1) ? (this.getColor(TAB_BORDER) || this.getColor(contrastBorder)) : null; tabContainer.style.outlineColor = this.getColor(activeContrastBorder);
tabContainer.style.outlineColor = this.getColor(activeContrastBorder);
const tabOptions = this.editorGroupService.getTabOptions();
const tabOptions = this.editorGroupService.getTabOptions(); ['off', 'left'].forEach(option => {
['off', 'left'].forEach(option => { const domAction = tabOptions.tabCloseButton === option ? DOM.addClass : DOM.removeClass;
const domAction = tabOptions.tabCloseButton === option ? DOM.addClass : DOM.removeClass; domAction(tabContainer, `close-button-${option}`);
domAction(tabContainer, `close-button-${option}`); });
});
// Label // Label
const tabLabel = this.editorLabels[index]; const tabLabel = this.editorLabels[index];
tabLabel.setLabel({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { extraClasses: ['tab-label'], italic: !isPinned }); tabLabel.setLabel({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { extraClasses: ['tab-label'], italic: !isPinned });
// Active state // Active state
if (isTabActive) { if (isTabActive) {
DOM.addClass(tabContainer, 'active'); DOM.addClass(tabContainer, 'active');
tabContainer.setAttribute('aria-selected', 'true'); tabContainer.setAttribute('aria-selected', 'true');
tabContainer.style.backgroundColor = this.getColor(TAB_ACTIVE_BACKGROUND); tabContainer.style.backgroundColor = this.getColor(TAB_ACTIVE_BACKGROUND);
tabLabel.element.style.color = this.getColor(isGroupActive ? TAB_ACTIVE_FOREGROUND : TAB_UNFOCUSED_ACTIVE_FOREGROUND); tabLabel.element.style.color = this.getColor(isGroupActive ? TAB_ACTIVE_FOREGROUND : TAB_UNFOCUSED_ACTIVE_FOREGROUND);
// Use boxShadow for the active tab border because if we also have a editor group header // Use boxShadow for the active tab border because if we also have a editor group header
// color, the two colors would collide and the tab border never shows up. // color, the two colors would collide and the tab border never shows up.
// see https://github.com/Microsoft/vscode/issues/33111 // see https://github.com/Microsoft/vscode/issues/33111
const activeTabBorderColor = this.getColor(isGroupActive ? TAB_ACTIVE_BORDER : TAB_UNFOCUSED_ACTIVE_BORDER); const activeTabBorderColor = this.getColor(isGroupActive ? TAB_ACTIVE_BORDER : TAB_UNFOCUSED_ACTIVE_BORDER);
if (activeTabBorderColor) { if (activeTabBorderColor) {
tabContainer.style.boxShadow = `${activeTabBorderColor} 0 -1px inset`; tabContainer.style.boxShadow = `${activeTabBorderColor} 0 -1px inset`;
} else {
tabContainer.style.boxShadow = null;
}
this.activeTab = tabContainer;
} else { } else {
DOM.removeClass(tabContainer, 'active');
tabContainer.setAttribute('aria-selected', 'false');
tabContainer.style.backgroundColor = this.getColor(TAB_INACTIVE_BACKGROUND);
tabLabel.element.style.color = this.getColor(isGroupActive ? TAB_INACTIVE_FOREGROUND : TAB_UNFOCUSED_INACTIVE_FOREGROUND);
tabContainer.style.boxShadow = null; tabContainer.style.boxShadow = null;
} }
// Dirty State this.activeTab = tabContainer;
if (isDirty) { } else {
DOM.addClass(tabContainer, 'dirty'); DOM.removeClass(tabContainer, 'active');
} else { tabContainer.setAttribute('aria-selected', 'false');
DOM.removeClass(tabContainer, 'dirty'); tabContainer.style.backgroundColor = this.getColor(TAB_INACTIVE_BACKGROUND);
} tabLabel.element.style.color = this.getColor(isGroupActive ? TAB_INACTIVE_FOREGROUND : TAB_UNFOCUSED_INACTIVE_FOREGROUND);
tabContainer.style.boxShadow = null;
}
// Dirty State
if (isDirty) {
DOM.addClass(tabContainer, 'dirty');
} else {
DOM.removeClass(tabContainer, 'dirty');
} }
}); });
...@@ -347,7 +350,7 @@ export class TabsTitleControl extends TitleControl { ...@@ -347,7 +350,7 @@ export class TabsTitleControl extends TitleControl {
this.updateEditorActionsToolbar(); this.updateEditorActionsToolbar();
// Ensure the active tab is always revealed // Ensure the active tab is always revealed
this.layout(); this.layout(this.dimension);
} }
private getTabLabels(editors: IEditorInput[]): IEditorInputLabel[] { private getTabLabels(editors: IEditorInput[]): IEditorInputLabel[] {
...@@ -538,12 +541,33 @@ export class TabsTitleControl extends TitleControl { ...@@ -538,12 +541,33 @@ export class TabsTitleControl extends TitleControl {
return tabContainer; return tabContainer;
} }
public layout(): void { public updateEditorActionsToolbar(): void {
if (!this.activeTab) { super.updateEditorActionsToolbar();
this.editorToolbarWidth = this.getElementWidth(this.editorToolbarContainer);
}
protected clearEditorActionsToolbar(): void {
super.clearEditorActionsToolbar();
this.editorToolbarWidth = this.getElementWidth(this.editorToolbarContainer);
}
private getElementWidth(element: HTMLElement): number {
// We are using getBoundingClientRect() over offsetWidth for a reason: only the former will return subpixel sizes
// whereas the other (offsetWidth) will round the value to the nearest number. For our layout code we really need
// the sizes with their fractions to not cause rounding issues.
return element.getBoundingClientRect().width;
}
public layout(dimension: Dimension): void {
if (!this.activeTab || !dimension) {
return; return;
} }
const visibleContainerWidth = this.tabsContainer.offsetWidth; this.dimension = dimension;
const visibleContainerWidth = this.dimension.width - this.editorToolbarWidth;
const totalContainerWidth = this.tabsContainer.scrollWidth; const totalContainerWidth = this.tabsContainer.scrollWidth;
// Update scrollbar // Update scrollbar
...@@ -559,7 +583,7 @@ export class TabsTitleControl extends TitleControl { ...@@ -559,7 +583,7 @@ export class TabsTitleControl extends TitleControl {
} }
// Reveal the active one // Reveal the active one
const containerScrollPosX = this.tabsContainer.scrollLeft; const containerScrollPosX = this.scrollbar.getScrollPosition().scrollLeft;
const activeTabPosX = this.activeTab.offsetLeft; const activeTabPosX = this.activeTab.offsetLeft;
const activeTabWidth = this.activeTab.offsetWidth; const activeTabWidth = this.activeTab.offsetWidth;
const activeTabFits = activeTabWidth <= visibleContainerWidth; const activeTabFits = activeTabWidth <= visibleContainerWidth;
...@@ -575,7 +599,7 @@ export class TabsTitleControl extends TitleControl { ...@@ -575,7 +599,7 @@ export class TabsTitleControl extends TitleControl {
// Tab is overlflowng to the left or does not fit: Scroll it into view to the left // Tab is overlflowng to the left or does not fit: Scroll it into view to the left
else if (containerScrollPosX > activeTabPosX || !activeTabFits) { else if (containerScrollPosX > activeTabPosX || !activeTabFits) {
this.scrollbar.setScrollPosition({ this.scrollbar.setScrollPosition({
scrollLeft: this.activeTab.offsetLeft scrollLeft: activeTabPosX
}); });
} }
} }
......
...@@ -45,6 +45,7 @@ import { IFileService } from 'vs/platform/files/common/files'; ...@@ -45,6 +45,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import URI from 'vs/base/common/uri'; import URI from 'vs/base/common/uri';
import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { isDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Dimension } from 'vs/base/browser/builder';
export interface IToolbarActions { export interface IToolbarActions {
primary: IAction[]; primary: IAction[];
...@@ -61,7 +62,7 @@ export interface ITitleAreaControl { ...@@ -61,7 +62,7 @@ export interface ITitleAreaControl {
refresh(instant?: boolean): void; refresh(instant?: boolean): void;
update(instant?: boolean): void; update(instant?: boolean): void;
updateEditorActionsToolbar(): void; updateEditorActionsToolbar(): void;
layout(): void; layout(dimension: Dimension): void;
dispose(): void; dispose(): void;
} }
...@@ -223,7 +224,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl ...@@ -223,7 +224,7 @@ export abstract class TitleControl extends Themable implements ITitleAreaControl
this.doRefresh(); this.doRefresh();
} }
public layout(): void { public layout(dimension: Dimension): void {
// Subclasses can opt in to react on layout // Subclasses can opt in to react on layout
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册