Revert "Merge from vscode 81d7885dc2e9dc617e1522697a2966bc4025a45d (#5949)" (#5983)

This reverts commit d15a3fcc98.
This commit is contained in:
Karl Burtram
2019-06-11 12:35:58 -07:00
committed by GitHub
parent 95a50b7892
commit 5a7562a37b
926 changed files with 11394 additions and 19540 deletions

View File

@@ -61,13 +61,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
//#endregion
private globalActionBar: ActionBar;
private globalActivityIdToActions: Map<string, GlobalActivityAction> = new Map();
private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; } = Object.create(null);
private cachedViewlets: ICachedViewlet[] = [];
private compositeBar: CompositeBar;
private compositeActions: Map<string, { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction }> = new Map();
private compositeActions: { [compositeId: string]: { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null);
private readonly viewletDisposables: Map<string, IDisposable> = new Map<string, IDisposable>();
constructor(
@@ -171,7 +169,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
throw illegalArgument('badge');
}
const action = this.globalActivityIdToActions.get(globalActivityId);
const action = this.globalActivityIdToActions[globalActivityId];
if (!action) {
throw illegalArgument('globalActivityId');
}
@@ -183,15 +181,14 @@ export class ActivitybarPart extends Part implements IActivityBarService {
createContentArea(parent: HTMLElement): HTMLElement {
this.element = parent;
const content = document.createElement('div');
addClass(content, 'content');
parent.appendChild(content);
// Viewlets action bar
// Top Actionbar with action items for each viewlet action
this.compositeBar.create(content);
// Global action bar
// Top Actionbar with action items for each viewlet action
const globalActivities = document.createElement('div');
addClass(globalActivities, 'global-activity');
content.appendChild(globalActivities);
@@ -211,7 +208,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder);
const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT;
container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : '';
container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : null;
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null;
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null;
container.style.borderRightColor = isPositionLeft ? borderColor : null;
@@ -246,13 +243,13 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}));
actions.forEach(a => {
this.globalActivityIdToActions.set(a.id, a);
this.globalActivityIdToActions[a.id] = a;
this.globalActionBar.push(a);
});
}
private getCompositeActions(compositeId: string): { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } {
let compositeActions = this.compositeActions.get(compositeId);
let compositeActions = this.compositeActions[compositeId];
if (!compositeActions) {
const viewlet = this.viewletService.getViewlet(compositeId);
if (viewlet) {
@@ -268,7 +265,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
};
}
this.compositeActions.set(compositeId, compositeActions);
this.compositeActions[compositeId] = compositeActions;
}
return compositeActions;
@@ -344,11 +341,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
private hideComposite(compositeId: string): void {
this.compositeBar.hideComposite(compositeId);
const compositeActions = this.compositeActions.get(compositeId);
const compositeActions = this.compositeActions[compositeId];
if (compositeActions) {
compositeActions.activityAction.dispose();
compositeActions.pinnedAction.dispose();
this.compositeActions.delete(compositeId);
delete this.compositeActions[compositeId];
}
}

View File

@@ -427,7 +427,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
});
}
private getContextMenuActions(): ReadonlyArray<IAction> {
private getContextMenuActions(): IAction[] {
const actions: IAction[] = this.model.visibleItems
.map(({ id, name, activityAction }) => (<IAction>{
id,

View File

@@ -549,11 +549,11 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
}));
// Activate on drag over to reveal targets
[this.badge, this.label].forEach(b => this._register(new DelayedDragHandler(b, () => {
[this.badge, this.label].forEach(b => new DelayedDragHandler(b, () => {
if (!this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && !this.getAction().checked) {
this.getAction().run();
}
})));
}));
this.updateStyles();
}

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/compositepart';
import * as nls from 'vs/nls';
import { defaultGenerator } from 'vs/base/common/idGenerator';
import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import * as strings from 'vs/base/common/strings';
import { Emitter } from 'vs/base/common/event';
import * as errors from 'vs/base/common/errors';
@@ -18,13 +18,13 @@ import { IAction } from 'vs/base/common/actions';
import { Part, IPartOptions } from 'vs/workbench/browser/part';
import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
import { IComposite } from 'vs/workbench/common/composite';
import { ScopedProgressService } from 'vs/workbench/services/progress/browser/localProgressService';
import { ScopedProgressService } from 'vs/workbench/services/progress/browser/progressService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILocalProgressService } from 'vs/platform/progress/common/progress';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -50,7 +50,7 @@ export interface ICompositeTitleLabel {
interface CompositeItem {
composite: Composite;
disposable: IDisposable;
localProgressService: ILocalProgressService;
progressService: IProgressService;
}
export abstract class CompositePart<T extends Composite> extends Part {
@@ -60,8 +60,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
protected toolBar: ToolBar;
private mapCompositeToCompositeContainer = new Map<string, HTMLElement>();
private mapActionsBindingToComposite = new Map<string, () => void>();
private mapCompositeToCompositeContainer: { [compositeId: string]: HTMLElement; };
private mapActionsBindingToComposite: { [compositeId: string]: () => void; };
private activeComposite: Composite | null;
private lastActiveCompositeId: string;
private instantiatedCompositeItems: Map<string, CompositeItem>;
@@ -91,6 +91,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
) {
super(id, options, themeService, storageService, layoutService);
this.mapCompositeToCompositeContainer = {};
this.mapActionsBindingToComposite = {};
this.activeComposite = null;
this.instantiatedCompositeItems = new Map<string, CompositeItem>();
this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId);
@@ -169,17 +171,17 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Instantiate composite from registry otherwise
const compositeDescriptor = this.registry.getComposite(id);
if (compositeDescriptor) {
const localProgressService = this.instantiationService.createInstance(ScopedProgressService, this.progressBar, compositeDescriptor.id, isActive);
const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection([ILocalProgressService, localProgressService]));
const progressService = this.instantiationService.createInstance(ScopedProgressService, this.progressBar, compositeDescriptor.id, isActive);
const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection([IProgressService, progressService]));
const composite = compositeDescriptor.instantiate(compositeInstantiationService);
const disposables = new DisposableStore();
const disposables: IDisposable[] = [];
// Remember as Instantiated
this.instantiatedCompositeItems.set(id, { composite, disposable: disposables, localProgressService });
this.instantiatedCompositeItems.set(id, { composite, disposable: toDisposable(() => dispose(disposables)), progressService });
// Register to title area update events from the composite
disposables.add(composite.onTitleAreaUpdate(() => this.onTitleAreaUpdate(composite.getId()), this));
composite.onTitleAreaUpdate(() => this.onTitleAreaUpdate(composite.getId()), this, disposables);
return composite;
}
@@ -204,7 +206,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
this.lastActiveCompositeId = this.activeComposite.getId();
// Composites created for the first time
let compositeContainer = this.mapCompositeToCompositeContainer.get(composite.getId());
let compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()];
if (!compositeContainer) {
// Build Container off-DOM
@@ -216,7 +218,13 @@ export abstract class CompositePart<T extends Composite> extends Part {
composite.updateStyles();
// Remember composite container
this.mapCompositeToCompositeContainer.set(composite.getId(), compositeContainer);
this.mapCompositeToCompositeContainer[composite.getId()] = compositeContainer;
}
// Report progress for slow loading composites (but only if we did not create the composites before already)
const compositeItem = this.instantiatedCompositeItems.get(composite.getId());
if (compositeItem && !compositeContainer) {
compositeItem.progressService.showWhile(Promise.resolve(), this.layoutService.isRestored() ? 800 : 3200 /* less ugly initial startup */);
}
// Fill Content and Actions
@@ -242,10 +250,10 @@ export abstract class CompositePart<T extends Composite> extends Part {
}
// Handle Composite Actions
let actionsBinding = this.mapActionsBindingToComposite.get(composite.getId());
let actionsBinding = this.mapActionsBindingToComposite[composite.getId()];
if (!actionsBinding) {
actionsBinding = this.collectCompositeActions(composite);
this.mapActionsBindingToComposite.set(composite.getId(), actionsBinding);
this.mapActionsBindingToComposite[composite.getId()] = actionsBinding;
}
actionsBinding();
@@ -298,13 +306,13 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Actions
const actionsBinding = this.collectCompositeActions(this.activeComposite);
this.mapActionsBindingToComposite.set(this.activeComposite.getId(), actionsBinding);
this.mapActionsBindingToComposite[this.activeComposite.getId()] = actionsBinding;
actionsBinding();
}
// Otherwise invalidate actions binding for next time when the composite becomes visible
else {
this.mapActionsBindingToComposite.delete(compositeId);
delete this.mapActionsBindingToComposite[compositeId];
}
}
@@ -358,16 +366,14 @@ export abstract class CompositePart<T extends Composite> extends Part {
const composite = this.activeComposite;
this.activeComposite = null;
const compositeContainer = this.mapCompositeToCompositeContainer.get(composite.getId());
const compositeContainer = this.mapCompositeToCompositeContainer[composite.getId()];
// Indicate to Composite
composite.setVisible(false);
// Take Container Off-DOM and hide
if (compositeContainer) {
compositeContainer.remove();
hide(compositeContainer);
}
compositeContainer.remove();
hide(compositeContainer);
// Clear any running Progress
this.progressBar.stop().hide();
@@ -456,17 +462,17 @@ export abstract class CompositePart<T extends Composite> extends Part {
return contentContainer;
}
getProgressIndicator(id: string): ILocalProgressService | null {
getProgressIndicator(id: string): IProgressService | null {
const compositeItem = this.instantiatedCompositeItems.get(id);
return compositeItem ? compositeItem.localProgressService : null;
return compositeItem ? compositeItem.progressService : null;
}
protected getActions(): ReadonlyArray<IAction> {
protected getActions(): IAction[] {
return [];
}
protected getSecondaryActions(): ReadonlyArray<IAction> {
protected getSecondaryActions(): IAction[] {
return [];
}
@@ -490,8 +496,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
return false; // do not remove active composite
}
this.mapCompositeToCompositeContainer.delete(compositeId);
this.mapActionsBindingToComposite.delete(compositeId);
delete this.mapCompositeToCompositeContainer[compositeId];
delete this.mapActionsBindingToComposite[compositeId];
const compositeItem = this.instantiatedCompositeItems.get(compositeId);
if (compositeItem) {
compositeItem.composite.dispose();
@@ -503,8 +509,8 @@ export abstract class CompositePart<T extends Composite> extends Part {
}
dispose(): void {
this.mapCompositeToCompositeContainer.clear();
this.mapActionsBindingToComposite.clear();
this.mapCompositeToCompositeContainer = null!; // StrictNullOverride: nulling out ok in dispose
this.mapActionsBindingToComposite = null!; // StrictNullOverride: nulling out ok in dispose
this.instantiatedCompositeItems.forEach(compositeItem => {
compositeItem.composite.dispose();

View File

@@ -15,7 +15,6 @@ import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { isEmptyObject } from 'vs/base/common/types';
import { DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
import { MementoObject } from 'vs/workbench/common/memento';
/**
* The base class of editors in the workbench. Editors register themselves for specific editor inputs.
@@ -178,7 +177,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
constructor(
private _id: string,
private key: string,
private memento: MementoObject,
private memento: object,
private limit: number,
private editorGroupService: IEditorGroupsService
) { }

View File

@@ -15,11 +15,11 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer';
import { URI } from 'vs/base/common/uri';
import { Dimension, size, clearNode } from 'vs/base/browser/dom';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { CancellationToken } from 'vs/base/common/cancellation';
import { dispose } from 'vs/base/common/lifecycle';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IFileService } from 'vs/platform/files/common/files';
export interface IOpenCallbacks {
openInternal: (input: EditorInput, options: EditorOptions) => Promise<void>;
@@ -48,7 +48,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
callbacks: IOpenCallbacks,
telemetryService: ITelemetryService,
themeService: IThemeService,
@IFileService private readonly fileService: IFileService,
@ITextFileService private readonly textFileService: ITextFileService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageService storageService: IStorageService
) {
@@ -89,11 +89,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
}
// Render Input
if (this.resourceViewerContext) {
this.resourceViewerContext.dispose();
}
this.resourceViewerContext = ResourceViewer.show({ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, this.fileService, this.binaryContainer, this.scrollbar, {
this.resourceViewerContext = ResourceViewer.show({ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, this.textFileService, this.binaryContainer, this.scrollbar, {
openInternalClb: () => this.handleOpenInternalCallback(input, options),
openExternalClb: this.environmentService.configuration.remoteAuthority ? undefined : resource => this.callbacks.openExternal(resource),
metadataClb: meta => this.handleMetadataChanged(meta)

View File

@@ -122,7 +122,7 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfigurat
'breadcrumbs.enabled': {
description: localize('enabled', "Enable/disable navigation breadcrumbs."),
type: 'boolean',
default: true
default: false
},
// 'breadcrumbs.useQuickPick': {
// description: localize('useQuickPick', "Use quick pick instead of breadcrumb-pickers."),

View File

@@ -384,14 +384,14 @@ export class BreadcrumbsControl {
this._breadcrumbsPickerShowing = true;
this._updateCkBreadcrumbsActive();
return combinedDisposable(
return combinedDisposable([
picker,
selectListener,
focusListener,
zoomListener,
focusTracker,
blurListener
);
]);
},
getAnchor: () => {
let maxInnerWidth = window.innerWidth - 8 /*a little less the full widget*/;
@@ -494,17 +494,17 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
category: localize('cmd.category', "View")
}
});
/* {{SQL CARBON EDIT}} - Disable unused menu item
MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
group: '5_editor',
order: 3,
command: {
id: 'breadcrumbs.toggle',
title: localize('miShowBreadcrumbs', "Show &&Breadcrumbs"),
toggled: ContextKeyExpr.equals('config.breadcrumbs.enabled', true)
}
});
*/
// {{SQL CARBON EDIT}} - Disable unused menu item
// MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, {
// group: '5_editor',
// order: 99,
// command: {
// id: 'breadcrumbs.toggle',
// title: localize('miToggleBreadcrumbs', "Toggle &&Breadcrumbs"),
// toggled: ContextKeyExpr.equals('config.breadcrumbs.enabled', true)
// }
// });
// {{SQL CARBON EDIT}} - End
CommandsRegistry.registerCommand('breadcrumbs.toggle', accessor => {
let config = accessor.get(IConfigurationService);
let value = BreadcrumbsConfig.IsEnabled.bindTo(config).getValue();

View File

@@ -11,7 +11,7 @@ import { IEditorQuickOpenEntry, IQuickOpenRegistry, Extensions as QuickOpenExten
import { StatusbarItemDescriptor, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext, EditorPinnedContext, EditorGroupEditorsCountContext } from 'vs/workbench/common/editor';
import { EditorInput, IEditorInputFactory, SideBySideEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, TextCompareEditorActiveContext } from 'vs/workbench/common/editor';
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
@@ -52,8 +52,6 @@ import { OpenWorkspaceButtonContribution } from 'vs/workbench/browser/parts/edit
import { ZoomStatusbarItem } from 'vs/workbench/browser/parts/editor/resourceViewer';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { toLocalResource } from 'vs/base/common/resources';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
// Register String Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
@@ -227,17 +225,11 @@ Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactor
registerEditorContribution(OpenWorkspaceButtonContribution);
// Register Editor Status
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatus, LifecyclePhase.Ready);
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(EditorStatus, StatusbarAlignment.RIGHT, 100 /* towards the left of the right hand side */));
// Register Zoom Status
const statusBar = Registry.as<IStatusbarRegistry>(StatusExtensions.Statusbar);
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(
ZoomStatusbarItem,
'status.imageZoom',
nls.localize('status.imageZoom', "Image Zoom"),
StatusbarAlignment.RIGHT,
101 /* to the left of editor status (100) */)
);
statusBar.registerStatusbarItem(new StatusbarItemDescriptor(ZoomStatusbarItem, StatusbarAlignment.RIGHT, 101 /* to the left of editor status (100) */));
// Register Status Actions
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
@@ -258,7 +250,7 @@ export class QuickOpenActionContributor extends ActionBarContributor {
return !!entry;
}
getActions(context: any): ReadonlyArray<IAction> {
getActions(context: any): IAction[] {
const actions: Action[] = [];
const entry = this.getEntry(context);
@@ -447,11 +439,11 @@ MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: edi
// Editor Title Context Menu
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITOR_COMMAND_ID, title: nls.localize('close', "Close") }, group: '1_close', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOthers', "Close Others"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 20 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRight', "Close to the Right"), precondition: EditorGroupEditorsCountContext.notEqualsTo('1') }, group: '1_close', order: 30, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeOthers', "Close Others") }, group: '1_close', order: 20 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, title: nls.localize('closeRight', "Close to the Right") }, group: '1_close', order: 30, when: ContextKeyExpr.has('config.workbench.editor.showTabs') });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '1_close', order: 40 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '1_close', order: 50 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepOpen', "Keep Open"), precondition: EditorPinnedContext.toNegated() }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.KEEP_EDITOR_COMMAND_ID, title: nls.localize('keepOpen', "Keep Open") }, group: '3_preview', order: 10, when: ContextKeyExpr.has('config.workbench.editor.enablePreview') });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_UP, title: nls.localize('splitUp', "Split Up") }, group: '5_split', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_DOWN, title: nls.localize('splitDown', "Split Down") }, group: '5_split', order: 20 });
MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { command: { id: editorCommands.SPLIT_EDITOR_LEFT, title: nls.localize('splitLeft', "Split Left") }, group: '5_split', order: 30 });

View File

@@ -11,7 +11,7 @@ import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } fr
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILocalProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
import { IProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
import { Event, Emitter } from 'vs/base/common/event';
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
@@ -47,11 +47,11 @@ export class EditorControl extends Disposable {
private groupView: IEditorGroupView,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ILocalProgressService localProgressService: ILocalProgressService
@IProgressService progressService: IProgressService
) {
super();
this.editorOperation = this._register(new LongRunningOperation(localProgressService));
this.editorOperation = this._register(new LongRunningOperation(progressService));
}
get activeControl(): IVisibleEditor | null {

View File

@@ -88,10 +88,10 @@ class DropOverlay extends Themable {
// Overlay contrast border (if any)
const activeContrastBorderColor = this.getColor(activeContrastBorder);
this.overlay.style.outlineColor = activeContrastBorderColor || '';
this.overlay.style.outlineOffset = activeContrastBorderColor ? '-2px' : '';
this.overlay.style.outlineStyle = activeContrastBorderColor ? 'dashed' : '';
this.overlay.style.outlineWidth = activeContrastBorderColor ? '2px' : '';
this.overlay.style.outlineColor = activeContrastBorderColor;
this.overlay.style.outlineOffset = activeContrastBorderColor ? '-2px' : null;
this.overlay.style.outlineStyle = activeContrastBorderColor ? 'dashed' : null;
this.overlay.style.outlineWidth = activeContrastBorderColor ? '2px' : null;
}
private registerListeners(): void {

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/editorgroupview';
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { EditorInput, EditorOptions, GroupIdentifier, ConfirmResult, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext } from 'vs/workbench/common/editor';
import { EditorInput, EditorOptions, GroupIdentifier, ConfirmResult, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor } from 'vs/workbench/common/editor';
import { Event, Emitter, Relay } from 'vs/base/common/event';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom';
@@ -20,8 +20,8 @@ import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BAC
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder, ICloseEditorOptions } from 'vs/workbench/services/editor/common/editorGroupsService';
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl';
import { ILocalProgressService } from 'vs/platform/progress/common/progress';
import { LocalProgressService } from 'vs/workbench/services/progress/browser/localProgressService';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { ProgressService } from 'vs/workbench/services/progress/browser/progressService';
import { localize } from 'vs/nls';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
@@ -184,7 +184,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
const scopedContextKeyService = this._register(this.contextKeyService.createScoped(this.element));
this.scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection(
[IContextKeyService, scopedContextKeyService],
[ILocalProgressService, new LocalProgressService(this.progressBar)]
[IProgressService, new ProgressService(this.progressBar)]
));
// Context keys
@@ -220,7 +220,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private handleGroupContextKeys(contextKeyServcie: IContextKeyService): void {
const groupActiveEditorDirtyContextKey = EditorGroupActiveEditorDirtyContext.bindTo(contextKeyServcie);
const groupEditorsCountContext = EditorGroupEditorsCountContext.bindTo(contextKeyServcie);
let activeEditorListener: IDisposable;
@@ -236,17 +235,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
};
// Update group contexts based on group changes
// Track the active editor and update context key that reflects
// the dirty state of this editor
this._register(this.onDidGroupChange(e => {
// Track the active editor and update context key that reflects
// the dirty state of this editor
if (e.kind === GroupChangeKind.EDITOR_ACTIVE) {
observeActiveEditor();
}
// Group editors count context
groupEditorsCountContext.set(this.count);
}));
observeActiveEditor();
@@ -291,17 +285,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
});
// Toolbar actions
const removeGroupAction = this._register(new Action(
CLOSE_EDITOR_GROUP_COMMAND_ID,
localize('closeGroupAction', "Close"),
'close-editor-group',
true,
() => {
this.accessor.removeGroup(this);
return Promise.resolve(true);
}));
const removeGroupAction = this._register(new Action(CLOSE_EDITOR_GROUP_COMMAND_ID, localize('closeGroupAction', "Close"), 'close-editor-group', true, () => { this.accessor.removeGroup(this); return Promise.resolve(true); }));
const keybinding = this.keybindingService.lookupKeybinding(removeGroupAction.id);
containerToolbar.push(removeGroupAction, { icon: true, label: false, keybinding: keybinding ? keybinding.getLabel() : undefined });
}
@@ -424,7 +408,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private async restoreEditors(from: IEditorGroupView | ISerializedEditorGroup): Promise<void> {
if (this._group.count === 0) {
return; // nothing to show
return Promise.resolve(); // nothing to show
}
// Determine editor options
@@ -437,7 +421,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
const activeEditor = this._group.activeEditor;
if (!activeEditor) {
return;
return Promise.resolve();
}
options.pinned = this._group.isPinned(activeEditor); // preserve pinned state
@@ -839,34 +823,30 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private async doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise<IEditor | null> {
// Show in editor control if the active editor changed
let openEditorPromise: Promise<IEditor | null>;
let openEditor: IEditor | null = null;
if (active) {
openEditorPromise = (async () => {
try {
const result = await this.editorControl.openEditor(editor, options);
try {
const result = await this.editorControl.openEditor(editor, options);
// Editor change event
if (result.editorChanged) {
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_ACTIVE, editor });
}
return result.control;
} catch (error) {
// Handle errors but do not bubble them up
this.doHandleOpenEditorError(error, editor, options);
return null; // error: return NULL as result to signal this
// Editor change event
if (result.editorChanged) {
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_ACTIVE, editor });
}
})();
openEditor = result.control;
} catch (error) {
// Handle errors but do not bubble them up
this.doHandleOpenEditorError(error, editor, options);
}
} else {
openEditorPromise = Promise.resolve(null); // inactive: return NULL as result to signal this
openEditor = null; // inactive: return NULL as result to signal this
}
// Show in title control after editor control because some actions depend on it
this.titleAreaControl.openEditor(editor);
return openEditorPromise;
return openEditor;
}
private doHandleOpenEditorError(error: Error, editor: EditorInput, options?: EditorOptions): void {
@@ -912,7 +892,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Use the first editor as active editor
const { editor, options } = editors.shift()!;
let firstOpenedEditor = await this.openEditor(editor, options);
let firstEditor = await this.openEditor(editor, options);
// Open the other ones inactive
const startingIndex = this.getIndexOfEditor(editor) + 1;
@@ -923,12 +903,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
adjustedEditorOptions.index = startingIndex + index;
const openedEditor = await this.openEditor(editor, adjustedEditorOptions);
if (!firstOpenedEditor) {
firstOpenedEditor = openedEditor; // only take if the first editor opening failed
if (!firstEditor) {
firstEditor = openedEditor; // only take if the first editor opening failed
}
}));
return firstOpenedEditor;
return firstEditor;
}
//#endregion
@@ -1128,7 +1108,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private async handleDirty(editors: EditorInput[]): Promise<boolean /* veto */> {
if (!editors.length) {
return false; // no veto
return Promise.resolve(false); // no veto
}
const editor = editors.shift()!;
@@ -1161,7 +1141,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this.accessor.groups.some(groupView => groupView !== this && groupView.group.contains(editor, true /* support side by side */)) || // editor is opened in other group
editor instanceof SideBySideEditorInput && this.isOpened(editor.master) // side by side editor master is still opened
) {
return false;
return Promise.resolve(false);
}
// Switch to editor that we want to handle and confirm to save/revert

View File

@@ -31,7 +31,6 @@ import { IView, orthogonal, LayoutPriority } from 'vs/base/browser/ui/grid/gridv
import { onUnexpectedError } from 'vs/base/common/errors';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { MementoObject } from 'vs/workbench/common/memento';
interface IEditorPartUIState {
serializedGrid: ISerializedGrid;
@@ -119,8 +118,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
private _preferredSize: Dimension | undefined;
private readonly workspaceMemento: MementoObject;
private readonly globalMemento: MementoObject;
private workspaceMemento: object;
private globalMemento: object;
private _partOptions: IEditorPartOptions;

View File

@@ -5,16 +5,17 @@
import 'vs/css!./media/editorstatus';
import * as nls from 'vs/nls';
import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { format } from 'vs/base/common/strings';
import { extname, basename } from 'vs/base/common/resources';
import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
import { areFunctions, withNullAsUndefined } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Action } from 'vs/base/common/actions';
import { Language } from 'vs/base/common/platform';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput, SideBySideEditor, IModeSupport } from 'vs/workbench/common/editor';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IEditorAction } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence } from 'vs/editor/common/model';
@@ -24,6 +25,7 @@ import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpa
import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor';
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
@@ -31,7 +33,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ITextFileService, SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
@@ -48,8 +50,6 @@ import { timeout } from 'vs/base/common/async';
import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { Event } from 'vs/base/common/event';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar';
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/workbench/services/queryEditor/browser/queryEditorService';
@@ -277,286 +277,148 @@ const nlsMultiSelectionRange = nls.localize('multiSelectionRange', "{0} selectio
const nlsMultiSelection = nls.localize('multiSelection', "{0} selections");
const nlsEOLLF = nls.localize('endOfLineLineFeed', "LF");
const nlsEOLCRLF = nls.localize('endOfLineCarriageReturnLineFeed', "CRLF");
const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus");
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized");
const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\".");
export class EditorStatus extends Disposable implements IWorkbenchContribution {
private tabFocusModeElement: IStatusbarEntryAccessor | null = null;
private screenRedearModeElement: IStatusbarEntryAccessor | null = null;
private indentationElement: IStatusbarEntryAccessor | null = null;
private selectionElement: IStatusbarEntryAccessor | null = null;
private encodingElement: IStatusbarEntryAccessor | null = null;
private eolElement: IStatusbarEntryAccessor | null = null;
private modeElement: IStatusbarEntryAccessor | null = null;
private metadataElement: IStatusbarEntryAccessor | null = null;
class StatusBarItem {
private _showing = true;
private readonly state = new State();
private readonly activeEditorListeners: IDisposable[] = [];
private delayedRender: IDisposable | null = null;
private toRender: StateChange | null = null;
private screenReaderNotification: INotificationHandle | null = null;
private promptedScreenReader: boolean = false;
constructor(
private readonly element: HTMLElement,
title: string,
) {
this.setVisible(false);
this.element.title = title;
}
set textContent(value: string) {
this.element.textContent = value;
}
set onclick(value: () => void) {
this.element.onclick = value;
}
setVisible(shouldShow: boolean): void {
if (shouldShow !== this._showing) {
this._showing = shouldShow;
this.element.style.display = shouldShow ? '' : 'none';
}
}
}
export class EditorStatus implements IStatusbarItem {
private state: State;
private element: HTMLElement;
private tabFocusModeElement: StatusBarItem;
private screenRedearModeElement: StatusBarItem;
private indentationElement: StatusBarItem;
private selectionElement: StatusBarItem;
private encodingElement: StatusBarItem;
private eolElement: StatusBarItem;
private modeElement: StatusBarItem;
private metadataElement: StatusBarItem;
private toDispose: IDisposable[];
private activeEditorListeners: IDisposable[];
private delayedRender: IDisposable | null;
private toRender: StateChange | null;
private screenReaderNotification: INotificationHandle | null;
constructor(
@IEditorService private readonly editorService: IEditorService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IQuickOpenService private readonly quickOpenService: IQuickOpenService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
@IModeService private readonly modeService: IModeService,
@ITextFileService private readonly textFileService: ITextFileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@INotificationService private readonly notificationService: INotificationService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IStatusbarService private readonly statusbarService: IStatusbarService
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
) {
super();
this.registerCommands();
this.registerListeners();
this.toDispose = [];
this.activeEditorListeners = [];
this.state = new State();
}
private registerListeners(): void {
this._register(this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()));
this._register(this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)));
this._register(this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange((e.resource))));
this._register(TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange()));
}
render(container: HTMLElement): IDisposable {
this.element = append(container, $('.editor-statusbar-item'));
private registerCommands(): void {
CommandsRegistry.registerCommand({ id: 'showEditorScreenReaderNotification', handler: () => this.showScreenReaderNotification() });
CommandsRegistry.registerCommand({ id: 'changeEditorIndentation', handler: () => this.showIndentationPicker() });
}
this.tabFocusModeElement = new StatusBarItem(
append(this.element, $('a.editor-status-tabfocusmode.status-bar-info')),
nls.localize('disableTabMode', "Disable Accessibility Mode"));
this.tabFocusModeElement.onclick = () => this.onTabFocusModeClick();
this.tabFocusModeElement.textContent = nlsTabFocusMode;
private showScreenReaderNotification(): void {
if (!this.screenReaderNotification) {
this.screenReaderNotification = this.notificationService.prompt(
Severity.Info,
nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio? (Certain features like folding, minimap or word wrap are disabled when using a screen reader)"),
[{
label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"),
run: () => {
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
}
}, {
label: nls.localize('screenReaderDetectedExplanation.answerNo', "No"),
run: () => {
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
}
}],
{ sticky: true }
);
this.screenRedearModeElement = new StatusBarItem(
append(this.element, $('a.editor-status-screenreadermode.status-bar-info')),
nlsScreenReaderDetectedTitle);
this.screenRedearModeElement.textContent = nlsScreenReaderDetected;
this.screenRedearModeElement.onclick = () => this.onScreenReaderModeClick();
Event.once(this.screenReaderNotification.onDidClose)(() => this.screenReaderNotification = null);
}
}
this.selectionElement = new StatusBarItem(
append(this.element, $('a.editor-status-selection')),
nls.localize('gotoLine', "Go to Line"));
this.selectionElement.onclick = () => this.onSelectionClick();
private async showIndentationPicker(): Promise<unknown> {
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
if (!activeTextEditorWidget) {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
this.indentationElement = new StatusBarItem(
append(this.element, $('a.editor-status-indentation')),
nls.localize('selectIndentation', "Select Indentation"));
this.indentationElement.onclick = () => this.onIndentationClick();
if (!isWritableCodeEditor(activeTextEditorWidget)) {
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
}
this.encodingElement = new StatusBarItem(
append(this.element, $('a.editor-status-encoding')),
nls.localize('selectEncoding', "Select Encoding"));
this.encodingElement.onclick = () => this.onEncodingClick();
const picks: QuickPickInput<IQuickPickItem & { run(): void }>[] = [
activeTextEditorWidget.getAction(IndentUsingSpaces.ID),
activeTextEditorWidget.getAction(IndentUsingTabs.ID),
activeTextEditorWidget.getAction(DetectIndentation.ID),
activeTextEditorWidget.getAction(IndentationToSpacesAction.ID),
activeTextEditorWidget.getAction(IndentationToTabsAction.ID),
activeTextEditorWidget.getAction(TrimTrailingWhitespaceAction.ID)
].map((a: IEditorAction) => {
return {
id: a.id,
label: a.label,
detail: Language.isDefaultVariant() ? undefined : a.alias,
run: () => {
activeTextEditorWidget.focus();
a.run();
this.eolElement = new StatusBarItem(
append(this.element, $('a.editor-status-eol')),
nls.localize('selectEOL', "Select End of Line Sequence"));
this.eolElement.onclick = () => this.onEOLClick();
this.modeElement = new StatusBarItem(
append(this.element, $('a.editor-status-mode')),
nls.localize('selectLanguageMode', "Select Language Mode"));
this.modeElement.onclick = () => this.onModeClick();
this.metadataElement = new StatusBarItem(
append(this.element, $('span.editor-status-metadata')),
nls.localize('fileInfo', "File Information"));
this.delayedRender = null;
this.toRender = null;
this.toDispose.push(
toDisposable(() => {
if (this.delayedRender) {
this.delayedRender.dispose();
this.delayedRender = null;
}
};
});
}),
this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()),
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
TabFocus.onDidChangeTabFocus(e => this.onTabFocusModeChange()),
);
picks.splice(3, 0, { type: 'separator', label: nls.localize('indentConvert', "convert file") });
picks.unshift({ type: 'separator', label: nls.localize('indentView', "change view") });
const action = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
return action && action.run();
}
private updateTabFocusModeElement(visible: boolean): void {
if (visible) {
if (!this.tabFocusModeElement) {
this.tabFocusModeElement = this.statusbarService.addEntry({
text: nls.localize('tabFocusModeEnabled', "Tab Moves Focus"),
tooltip: nls.localize('disableTabMode', "Disable Accessibility Mode"),
command: 'editor.action.toggleTabFocusMode'
}, 'status.editor.tabFocusMode', nls.localize('status.editor.tabFocusMode', "Accessibility Mode"), StatusbarAlignment.RIGHT, 100.7);
}
} else {
if (this.tabFocusModeElement) {
this.tabFocusModeElement.dispose();
this.tabFocusModeElement = null;
}
}
}
private updateScreenReaderModeElement(visible: boolean): void {
if (visible) {
if (!this.screenRedearModeElement) {
this.screenRedearModeElement = this.statusbarService.addEntry({
text: nls.localize('screenReaderDetected', "Screen Reader Optimized"),
tooltip: nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\"."),
command: 'showEditorScreenReaderNotification'
}, 'status.editor.screenReaderMode', nls.localize('status.editor.screenReaderMode', "Screen Reader Mode"), StatusbarAlignment.RIGHT, 100.6);
}
} else {
if (this.screenRedearModeElement) {
this.screenRedearModeElement.dispose();
this.screenRedearModeElement = null;
}
}
}
private updateSelectionElement(text: string | undefined): void {
if (!text) {
if (this.selectionElement) {
dispose(this.selectionElement);
this.selectionElement = null;
}
return;
}
const props = {
text,
tooltip: nls.localize('gotoLine', "Go to Line"),
command: 'workbench.action.gotoLine'
};
this.selectionElement = this.updateElement(this.selectionElement, props, 'status.editor.selection', nls.localize('status.editor.selection', "Editor Selection"), StatusbarAlignment.RIGHT, 100.5);
}
private updateIndentationElement(text: string | undefined): void {
if (!text) {
if (this.indentationElement) {
dispose(this.indentationElement);
this.indentationElement = null;
}
return;
}
const props = {
text,
tooltip: nls.localize('selectIndentation', "Select Indentation"),
command: 'changeEditorIndentation'
};
this.indentationElement = this.updateElement(this.indentationElement, props, 'status.editor.indentation', nls.localize('status.editor.indentation', "Editor Indentation"), StatusbarAlignment.RIGHT, 100.4);
}
private updateEncodingElement(text: string | undefined): void {
if (!text) {
if (this.encodingElement) {
dispose(this.encodingElement);
this.encodingElement = null;
}
return;
}
const props = {
text,
tooltip: nls.localize('selectEncoding', "Select Encoding"),
command: 'workbench.action.editor.changeEncoding'
};
this.encodingElement = this.updateElement(this.encodingElement, props, 'status.editor.encoding', nls.localize('status.editor.encoding', "Editor Encoding"), StatusbarAlignment.RIGHT, 100.3);
}
private updateEOLElement(text: string | undefined): void {
if (!text) {
if (this.eolElement) {
dispose(this.eolElement);
this.eolElement = null;
}
return;
}
const props = {
text,
tooltip: nls.localize('selectEOL', "Select End of Line Sequence"),
command: 'workbench.action.editor.changeEOL'
};
this.eolElement = this.updateElement(this.eolElement, props, 'status.editor.eol', nls.localize('status.editor.eol', "Editor End of Line"), StatusbarAlignment.RIGHT, 100.2);
}
private updateModeElement(text: string | undefined): void {
if (!text) {
if (this.modeElement) {
dispose(this.modeElement);
this.modeElement = null;
}
return;
}
const props = {
text,
tooltip: nls.localize('selectLanguageMode', "Select Language Mode"),
command: 'workbench.action.editor.changeLanguageMode'
};
this.modeElement = this.updateElement(this.modeElement, props, 'status.editor.mode', nls.localize('status.editor.mode', "Editor Language"), StatusbarAlignment.RIGHT, 100.1);
}
private updateMetadataElement(text: string | undefined): void {
if (!text) {
if (this.metadataElement) {
dispose(this.metadataElement);
this.metadataElement = null;
}
return;
}
const props = {
text,
tooltip: nls.localize('fileInfo', "File Information")
};
this.metadataElement = this.updateElement(this.metadataElement, props, 'status.editor.info', nls.localize('status.editor.info', "File Information"), StatusbarAlignment.RIGHT, 100);
}
private updateElement(element: IStatusbarEntryAccessor | null, props: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number): IStatusbarEntryAccessor | null {
if (!element) {
element = this.statusbarService.addEntry(props, id, name, alignment, priority);
} else {
element.update(props);
}
return element;
return combinedDisposable(this.toDispose);
}
private updateState(update: StateDelta): void {
const changed = this.state.update(update);
if (!changed.hasChanges()) {
return; // Nothing really changed
// Nothing really changed
return;
}
if (!this.toRender) {
this.toRender = changed;
this.delayedRender = runAtThisOrScheduleAtNextAnimationFrame(() => {
this.delayedRender = null;
const toRender = this.toRender;
this.toRender = null;
if (toRender) {
this.doRenderNow(toRender);
this._renderNow(toRender);
}
});
} else {
@@ -564,15 +426,68 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
}
private doRenderNow(changed: StateChange): void {
this.updateTabFocusModeElement(!!this.state.tabFocusMode);
this.updateScreenReaderModeElement(!!this.state.screenReaderMode);
this.updateIndentationElement(this.state.indentation);
this.updateSelectionElement(this.state.selectionStatus && !this.state.screenReaderMode ? this.state.selectionStatus : undefined);
this.updateEncodingElement(this.state.encoding);
this.updateEOLElement(this.state.EOL ? this.state.EOL === '\r\n' ? nlsEOLCRLF : nlsEOLLF : undefined);
this.updateModeElement(this.state.mode);
this.updateMetadataElement(this.state.metadata);
private _renderNow(changed: StateChange): void {
if (changed.tabFocusMode) {
this.tabFocusModeElement.setVisible(!!this.state.tabFocusMode);
}
if (changed.screenReaderMode) {
this.screenRedearModeElement.setVisible(!!this.state.screenReaderMode);
}
if (changed.indentation) {
if (this.state.indentation) {
this.indentationElement.textContent = this.state.indentation;
this.indentationElement.setVisible(true);
} else {
this.indentationElement.setVisible(false);
}
}
if (changed.selectionStatus) {
if (this.state.selectionStatus && !this.state.screenReaderMode) {
this.selectionElement.textContent = this.state.selectionStatus;
this.selectionElement.setVisible(true);
} else {
this.selectionElement.setVisible(false);
}
}
if (changed.encoding) {
if (this.state.encoding) {
this.encodingElement.textContent = this.state.encoding;
this.encodingElement.setVisible(true);
} else {
this.encodingElement.setVisible(false);
}
}
if (changed.EOL) {
if (this.state.EOL) {
this.eolElement.textContent = this.state.EOL === '\r\n' ? nlsEOLCRLF : nlsEOLLF;
this.eolElement.setVisible(true);
} else {
this.eolElement.setVisible(false);
}
}
if (changed.mode) {
if (this.state.mode) {
this.modeElement.textContent = this.state.mode;
this.modeElement.setVisible(true);
} else {
this.modeElement.setVisible(false);
}
}
if (changed.metadata) {
if (this.state.metadata) {
this.metadataElement.textContent = this.state.metadata;
this.metadataElement.setVisible(true);
} else {
this.metadataElement.setVisible(false);
}
}
}
private getSelectionLabel(info: IEditorSelectionStatus): string | undefined {
@@ -599,6 +514,66 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
return undefined;
}
private onModeClick(): void {
const action = this.instantiationService.createInstance(ChangeModeAction, ChangeModeAction.ID, ChangeModeAction.LABEL);
action.run();
action.dispose();
}
private onIndentationClick(): void {
const action = this.instantiationService.createInstance(ChangeIndentationAction, ChangeIndentationAction.ID, ChangeIndentationAction.LABEL);
action.run();
action.dispose();
}
private onScreenReaderModeClick(): void {
if (!this.screenReaderNotification) {
this.screenReaderNotification = this.notificationService.prompt(
Severity.Info,
nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio? (Certain features like folding, minimap or word wrap are disabled when using a screen reader)"),
[{
label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"),
run: () => {
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
}
}, {
label: nls.localize('screenReaderDetectedExplanation.answerNo', "No"),
run: () => {
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
}
}],
{ sticky: true }
);
Event.once(this.screenReaderNotification.onDidClose)(() => {
this.screenReaderNotification = null;
});
}
}
private onSelectionClick(): void {
this.quickOpenService.show(':'); // "Go to line"
}
private onEOLClick(): void {
const action = this.instantiationService.createInstance(ChangeEOLAction, ChangeEOLAction.ID, ChangeEOLAction.LABEL);
action.run();
action.dispose();
}
private onEncodingClick(): void {
const action = this.instantiationService.createInstance(ChangeEncodingAction, ChangeEncodingAction.ID, ChangeEncodingAction.LABEL);
action.run();
action.dispose();
}
private onTabFocusModeClick(): void {
TabFocus.setTabFocusMode(false);
}
private updateStatusBar(): void {
const activeControl = this.editorService.activeControl;
const activeCodeEditor = activeControl ? withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
@@ -692,6 +667,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
if (editorWidget) {
const textModel = editorWidget.getModel();
if (textModel) {
// Compute mode
const modeId = textModel.getLanguageIdentifier().language;
info = { mode: this.modeService.getLanguageName(modeId) || undefined };
}
@@ -728,6 +704,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.updateState(update);
}
private promptedScreenReader: boolean = false;
private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void {
let screenReaderMode = false;
@@ -737,9 +715,12 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
if (screenReaderDetected) {
const screenReaderConfiguration = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
if (screenReaderConfiguration === 'auto') {
// show explanation
if (!this.promptedScreenReader) {
this.promptedScreenReader = true;
setTimeout(() => this.showScreenReaderNotification(), 100);
setTimeout(() => {
this.onScreenReaderModeClick();
}, 100);
}
}
}
@@ -755,7 +736,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
}
private onSelectionChange(editorWidget: ICodeEditor | undefined): void {
const info: IEditorSelectionStatus = Object.create(null);
const info: IEditorSelectionStatus = {};
// We only support text based editors
if (editorWidget) {
@@ -849,15 +830,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
return !!activeControl && activeControl === control;
}
dispose(): void {
super.dispose();
if (this.delayedRender) {
this.delayedRender.dispose();
this.delayedRender = null;
}
}
}
function isWritableCodeEditor(codeEditor: ICodeEditor | undefined): boolean {
@@ -927,7 +899,7 @@ export class ChangeModeAction extends Action {
// Compute mode
let currentModeId: string | undefined;
let modeId: string | undefined;
let modeId: string;
if (textModel) {
modeId = textModel.getLanguageIdentifier().language;
currentModeId = this.modeService.getLanguageName(modeId) || undefined;
@@ -967,9 +939,9 @@ export class ChangeModeAction extends Action {
}
// Offer action to configure via settings
let configureModeAssociations: IQuickPickItem | undefined;
let configureModeSettings: IQuickPickItem | undefined;
let galleryAction: Action | undefined;
let configureModeAssociations: IQuickPickItem;
let configureModeSettings: IQuickPickItem;
let galleryAction: Action;
if (hasLanguageSupport && resource) {
const ext = extname(resource) || basename(resource);
@@ -993,60 +965,61 @@ export class ChangeModeAction extends Action {
picks.unshift(autoDetectMode);
}
const pick = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode"), matchOnDescription: true });
if (!pick) {
return;
}
if (pick === galleryAction) {
galleryAction.run();
return;
}
// User decided to permanently configure associations, return right after
if (pick === configureModeAssociations) {
if (resource) {
this.configureFileAssociation(resource);
return this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguage', "Select Language Mode"), matchOnDescription: true }).then(pick => {
if (!pick) {
return;
}
return;
}
// User decided to configure settings for current language
if (pick === configureModeSettings) {
this.preferencesService.configureSettingsForLanguage(withUndefinedAsNull(modeId));
return;
}
if (pick === galleryAction) {
galleryAction.run();
return;
}
// Change mode for active editor
const activeEditor = this.editorService.activeControl; // {{SQL CARBON EDIT}} @anthonydresser change to activeControl from active editor
if (activeEditor) {
const modeSupport = toEditorWithModeSupport(activeEditor.input); // {{SQL CARBON EDIT}} @anthonydresser reference input rather than activeeditor directly
if (modeSupport) {
// User decided to permanently configure associations, return right after
if (pick === configureModeAssociations) {
if (resource) {
this.configureFileAssociation(resource);
}
return;
}
// Find mode
let languageSelection: ILanguageSelection | undefined;
if (pick === autoDetectMode) {
if (textModel) {
const resource = toResource(activeEditor.input, { supportSideBySide: SideBySideEditor.MASTER }); // {{SQL CARBON EDIT}} @anthonydresser reference input rather than activeeditor directly
if (resource) {
languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1));
// User decided to configure settings for current language
if (pick === configureModeSettings) {
this.preferencesService.configureSettingsForLanguage(modeId);
return;
}
// Change mode for active editor
const activeEditor = this.editorService.activeControl; // {{SQL CARBON EDIT}} @anthonydresser change to activeControl from active editor
if (activeEditor) {
const modeSupport = toEditorWithModeSupport(activeEditor.input); // {{SQL CARBON EDIT}} @anthonydresser reference input rather than activeeditor directly
if (modeSupport) {
// Find mode
let languageSelection: ILanguageSelection | undefined;
if (pick === autoDetectMode) {
if (textModel) {
const resource = toResource(activeEditor.input, { supportSideBySide: SideBySideEditor.MASTER }); // {{SQL CARBON EDIT}} @anthonydresser reference input rather than activeeditor directly
if (resource) {
languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1));
}
}
} else {
languageSelection = this.modeService.createByLanguageName(pick.label);
}
} else {
languageSelection = this.modeService.createByLanguageName(pick.label);
}
// {{SQL CARBON EDIT}} @anthonydresser preform a check before we actuall set the mode
// Change mode
if (typeof languageSelection !== 'undefined') {
QueryEditorService.sqlLanguageModeCheck(textModel, languageSelection, activeEditor).then(newTextModel => {
if (newTextModel) {
modeSupport.setMode(languageSelection.languageIdentifier.language);
}
});
// {{SQL CARBON EDIT}} @anthonydresser preform a check before we actuall set the mode
// Change mode
if (typeof languageSelection !== 'undefined') {
QueryEditorService.sqlLanguageModeCheck(textModel, languageSelection, activeEditor).then(newTextModel => {
if (newTextModel) {
modeSupport.setMode(languageSelection.languageIdentifier.language);
}
});
}
}
}
}
});
}
private configureFileAssociation(resource: URI): void {
@@ -1097,6 +1070,57 @@ export interface IChangeEOLEntry extends IQuickPickItem {
eol: EndOfLineSequence;
}
class ChangeIndentationAction extends Action {
static readonly ID = 'workbench.action.editor.changeIndentation';
static readonly LABEL = nls.localize('changeIndentation', "Change Indentation");
constructor(
actionId: string,
actionLabel: string,
@IEditorService private readonly editorService: IEditorService,
@IQuickInputService private readonly quickInputService: IQuickInputService
) {
super(actionId, actionLabel);
}
async run(): Promise<any> {
const activeTextEditorWidget = getCodeEditor(this.editorService.activeTextEditorWidget);
if (!activeTextEditorWidget) {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
if (!isWritableCodeEditor(activeTextEditorWidget)) {
return this.quickInputService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]);
}
const picks: QuickPickInput<IQuickPickItem & { run(): void }>[] = [
activeTextEditorWidget.getAction(IndentUsingSpaces.ID),
activeTextEditorWidget.getAction(IndentUsingTabs.ID),
activeTextEditorWidget.getAction(DetectIndentation.ID),
activeTextEditorWidget.getAction(IndentationToSpacesAction.ID),
activeTextEditorWidget.getAction(IndentationToTabsAction.ID),
activeTextEditorWidget.getAction(TrimTrailingWhitespaceAction.ID)
].map((a: IEditorAction) => {
return {
id: a.id,
label: a.label,
detail: Language.isDefaultVariant() ? undefined : a.alias,
run: () => {
activeTextEditorWidget.focus();
a.run();
}
};
});
picks.splice(3, 0, { type: 'separator', label: nls.localize('indentConvert', "convert file") });
picks.unshift({ type: 'separator', label: nls.localize('indentView', "change view") });
const action = await this.quickInputService.pick(picks, { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
return action && action.run();
}
}
export class ChangeEOLAction extends Action {
static readonly ID = 'workbench.action.editor.changeEOL';
@@ -1158,7 +1182,7 @@ export class ChangeEncodingAction extends Action {
super(actionId, actionLabel);
}
async run(): Promise<any> {
run(): Promise<any> {
if (!getCodeEditor(this.editorService.activeTextEditorWidget)) {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
@@ -1183,88 +1207,93 @@ export class ChangeEncodingAction extends Action {
reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding"), detail: 'Reopen with Encoding' };
}
let action: IQuickPickItem;
let pickActionPromise: Promise<IQuickPickItem>;
if (encodingSupport instanceof UntitledEditorInput) {
action = saveWithEncodingPick;
pickActionPromise = Promise.resolve(saveWithEncodingPick);
} else if (!isWritableBaseEditor(activeControl)) {
action = reopenWithEncodingPick;
pickActionPromise = Promise.resolve(reopenWithEncodingPick);
} else {
action = await this.quickInputService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
pickActionPromise = this.quickInputService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true });
}
if (!action) {
return;
}
return pickActionPromise.then(action => {
if (!action) {
return undefined;
}
await timeout(50); // quick open is sensitive to being opened so soon after another
const resource = toResource(activeControl!.input, { supportSideBySide: SideBySideEditor.MASTER });
const resource = toResource(activeControl!.input, { supportSideBySide: SideBySideEditor.MASTER });
if (!resource || !this.fileService.canHandleResource(resource)) {
return null; // encoding detection only possible for resources the file service can handle
}
return timeout(50 /* quick open is sensitive to being opened so soon after another */)
.then(() => {
if (!resource || !this.fileService.canHandleResource(resource)) {
return Promise.resolve(null); // encoding detection only possible for resources the file service can handle
}
const content = await this.textFileService.read(resource, { autoGuessEncoding: true, acceptTextOnly: true });
const guessedEncoding = content.encoding;
return this.textFileService.read(resource, { autoGuessEncoding: true, acceptTextOnly: true }).then(content => content.encoding, err => null);
})
.then((guessedEncoding: string) => {
const isReopenWithEncoding = (action === reopenWithEncodingPick);
const isReopenWithEncoding = (action === reopenWithEncodingPick);
const configuredEncoding = this.textResourceConfigurationService.getValue(withNullAsUndefined(resource), 'files.encoding');
const configuredEncoding = this.textResourceConfigurationService.getValue(withNullAsUndefined(resource), 'files.encoding');
let directMatchIndex: number | undefined;
let aliasMatchIndex: number | undefined;
let directMatchIndex: number | undefined;
let aliasMatchIndex: number | undefined;
// All encodings are valid picks
const picks: QuickPickInput[] = Object.keys(SUPPORTED_ENCODINGS)
.sort((k1, k2) => {
if (k1 === configuredEncoding) {
return -1;
} else if (k2 === configuredEncoding) {
return 1;
}
// All encodings are valid picks
const picks: QuickPickInput[] = Object.keys(SUPPORTED_ENCODINGS)
.sort((k1, k2) => {
if (k1 === configuredEncoding) {
return -1;
} else if (k2 === configuredEncoding) {
return 1;
}
return SUPPORTED_ENCODINGS[k1].order - SUPPORTED_ENCODINGS[k2].order;
})
.filter(k => {
if (k === guessedEncoding && guessedEncoding !== configuredEncoding) {
return false; // do not show encoding if it is the guessed encoding that does not match the configured
}
return SUPPORTED_ENCODINGS[k1].order - SUPPORTED_ENCODINGS[k2].order;
})
.filter(k => {
if (k === guessedEncoding && guessedEncoding !== configuredEncoding) {
return false; // do not show encoding if it is the guessed encoding that does not match the configured
}
return !isReopenWithEncoding || !SUPPORTED_ENCODINGS[k].encodeOnly; // hide those that can only be used for encoding if we are about to decode
})
.map((key, index) => {
if (key === encodingSupport.getEncoding()) {
directMatchIndex = index;
} else if (SUPPORTED_ENCODINGS[key].alias === encodingSupport.getEncoding()) {
aliasMatchIndex = index;
}
return !isReopenWithEncoding || !SUPPORTED_ENCODINGS[k].encodeOnly; // hide those that can only be used for encoding if we are about to decode
})
.map((key, index) => {
if (key === encodingSupport.getEncoding()) {
directMatchIndex = index;
} else if (SUPPORTED_ENCODINGS[key].alias === encodingSupport.getEncoding()) {
aliasMatchIndex = index;
}
return { id: key, label: SUPPORTED_ENCODINGS[key].labelLong, description: key };
});
return { id: key, label: SUPPORTED_ENCODINGS[key].labelLong, description: key };
});
const items = picks.slice() as IQuickPickItem[];
const items = picks.slice() as IQuickPickItem[];
// If we have a guessed encoding, show it first unless it matches the configured encoding
if (guessedEncoding && configuredEncoding !== guessedEncoding && SUPPORTED_ENCODINGS[guessedEncoding]) {
picks.unshift({ type: 'separator' });
picks.unshift({ id: guessedEncoding, label: SUPPORTED_ENCODINGS[guessedEncoding].labelLong, description: nls.localize('guessedEncoding', "Guessed from content") });
}
// If we have a guessed encoding, show it first unless it matches the configured encoding
if (guessedEncoding && configuredEncoding !== guessedEncoding && SUPPORTED_ENCODINGS[guessedEncoding]) {
picks.unshift({ type: 'separator' });
picks.unshift({ id: guessedEncoding, label: SUPPORTED_ENCODINGS[guessedEncoding].labelLong, description: nls.localize('guessedEncoding', "Guessed from content") });
}
return this.quickInputService.pick(picks, {
placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1]
}).then(encoding => {
if (!encoding) {
return;
}
const encoding = await this.quickInputService.pick(picks, {
placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1]
const activeControl = this.editorService.activeControl;
if (!activeControl) {
return;
}
const encodingSupport = toEditorWithEncodingSupport(activeControl.input);
if (typeof encoding.id !== 'undefined' && encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
}
});
});
});
if (!encoding) {
return;
}
if (!this.editorService.activeControl) {
return;
}
const activeEncodingSupport = toEditorWithEncodingSupport(this.editorService.activeControl.input);
if (typeof encoding.id !== 'undefined' && activeEncodingSupport && activeEncodingSupport.getEncoding() !== encoding.id) {
activeEncodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
}
}
}

View File

@@ -3,6 +3,25 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .editor-statusbar-item > a:not(:first-child) {
margin-left: 5px;
}
.monaco-workbench .editor-statusbar-item > .editor-status-mode,
.monaco-workbench .editor-statusbar-item > .editor-status-encoding,
.monaco-workbench .editor-statusbar-item > .editor-status-eol,
.monaco-workbench .editor-statusbar-item > .editor-status-selection,
.monaco-workbench .editor-statusbar-item > .editor-status-indentation,
.monaco-workbench .editor-statusbar-item > .editor-status-metadata,
.monaco-workbench .editor-statusbar-item > .editor-status-tabfocusmode,
.monaco-workbench .editor-statusbar-item > .editor-status-screenreadermode {
padding: 0 5px 0 5px;
}
.monaco-workbench .editor-statusbar-item > .editor-status-metadata {
cursor: default !important;
}
.monaco-workbench .screen-reader-detected-explanation {
width: 420px;
top: 30px;

View File

@@ -14,23 +14,19 @@
.monaco-resource-viewer.image {
padding: 0;
background-position: 0 0, 8px 8px;
background-size: 16px 16px;
display: flex;
box-sizing: border-box;
}
.monaco-resource-viewer.image img {
padding: 0;
background-position: 0 0, 8px 8px;
background-size: 16px 16px;
}
.vs .monaco-resource-viewer.image img {
.vs .monaco-resource-viewer.image {
background-image:
linear-gradient(45deg, rgb(230, 230, 230) 25%, transparent 25%, transparent 75%, rgb(230, 230, 230) 75%, rgb(230, 230, 230)),
linear-gradient(45deg, rgb(230, 230, 230) 25%, transparent 25%, transparent 75%, rgb(230, 230, 230) 75%, rgb(230, 230, 230));
}
.vs-dark .monaco-resource-viewer.image img {
.vs-dark .monaco-resource-viewer.image {
background-image:
linear-gradient(45deg, rgb(20, 20, 20) 25%, transparent 25%, transparent 75%, rgb(20, 20, 20) 75%, rgb(20, 20, 20)),
linear-gradient(45deg, rgb(20, 20, 20) 25%, transparent 25%, transparent 75%, rgb(20, 20, 20) 75%, rgb(20, 20, 20));
@@ -58,10 +54,6 @@
cursor: zoom-out;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item > a.zoom-statusbar-item {
padding: 0 5px 0 5px;
}
.monaco-resource-viewer .embedded-link,
.monaco-resource-viewer .embedded-link:hover {
cursor: pointer;

View File

@@ -47,7 +47,7 @@ export class NoTabsTitleControl extends TitleControl {
// Breadcrumbs
this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, breadcrumbsBackground: () => Color.transparent });
toggleClass(this.titleContainer, 'breadcrumbs', Boolean(this.breadcrumbsControl));
this._register({ dispose: () => removeClass(this.titleContainer, 'breadcrumbs') }); // import to remove because the container is a shared dom node
this.toDispose.push({ dispose: () => removeClass(this.titleContainer, 'breadcrumbs') }); // import to remove because the container is a shared dom node
// Right Actions Container
const actionsContainer = document.createElement('div');

View File

@@ -15,13 +15,13 @@ import { clamp } from 'vs/base/common/numbers';
import { Themable } from 'vs/workbench/common/theme';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IDisposable, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IDisposable, Disposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Action } from 'vs/base/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { memoize } from 'vs/base/common/decorators';
import * as platform from 'vs/base/common/platform';
import { IFileService } from 'vs/platform/files/common/files';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
export interface IResourceDescriptor {
readonly resource: URI;
@@ -78,7 +78,7 @@ export class ResourceViewer {
static show(
descriptor: IResourceDescriptor,
fileService: IFileService,
textFileService: ITextFileService,
container: HTMLElement,
scrollbar: DomScrollableElement,
delegate: ResourceViewerDelegate
@@ -89,7 +89,7 @@ export class ResourceViewer {
// Images
if (ResourceViewer.isImageResource(descriptor)) {
return ImageView.create(container, descriptor, fileService, scrollbar, delegate);
return ImageView.create(container, descriptor, textFileService, scrollbar, delegate);
}
// Large Files
@@ -118,12 +118,12 @@ class ImageView {
static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
fileService: IFileService,
textFileService: ITextFileService,
scrollbar: DomScrollableElement,
delegate: ResourceViewerDelegate
): ResourceViewerContext {
if (ImageView.shouldShowImageInline(descriptor)) {
return InlineImageView.create(container, descriptor, fileService, scrollbar, delegate);
return InlineImageView.create(container, descriptor, textFileService, scrollbar, delegate);
}
return LargeImageView.create(container, descriptor, delegate);
@@ -160,7 +160,7 @@ class LargeImageView {
DOM.clearNode(container);
const disposables = new DisposableStore();
const disposables: IDisposable[] = [];
const label = document.createElement('p');
label.textContent = nls.localize('largeImageError', "The image is not displayed in the editor because it is too large ({0}).", size);
@@ -172,10 +172,10 @@ class LargeImageView {
link.setAttribute('role', 'button');
link.textContent = nls.localize('resourceOpenExternalButton', "Open image using external program?");
disposables.add(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => openExternal(descriptor.resource)));
disposables.push(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => openExternal(descriptor.resource)));
}
return disposables;
return combinedDisposable(disposables);
}
}
@@ -212,7 +212,7 @@ class FileSeemsBinaryFileView {
DOM.clearNode(container);
const disposables = new DisposableStore();
const disposables: IDisposable[] = [];
const label = document.createElement('p');
label.textContent = nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding.");
@@ -223,12 +223,12 @@ class FileSeemsBinaryFileView {
link.setAttribute('role', 'button');
link.textContent = nls.localize('openAsText', "Do you want to open it anyway?");
disposables.add(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => delegate.openInternalClb(descriptor.resource)));
disposables.push(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => delegate.openInternalClb(descriptor.resource)));
}
scrollbar.scanDomNode();
return disposables;
return combinedDisposable(disposables);
}
}
@@ -359,15 +359,15 @@ class InlineImageView {
static create(
container: HTMLElement,
descriptor: IResourceDescriptor,
fileService: IFileService,
textFileService: ITextFileService,
scrollbar: DomScrollableElement,
delegate: ResourceViewerDelegate
) {
const disposables = new DisposableStore();
const disposables: IDisposable[] = [];
const context: ResourceViewerContext = {
layout(dimension: DOM.Dimension) { },
dispose: () => disposables.dispose()
dispose: () => combinedDisposable(disposables).dispose()
};
const cacheKey = `${descriptor.resource.toString()}:${descriptor.etag}`;
@@ -436,7 +436,7 @@ class InlineImageView {
updateScale(scale);
}
disposables.add(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
disposables.push(DOM.addDisposableListener(window, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => {
if (!image) {
return;
}
@@ -449,7 +449,7 @@ class InlineImageView {
}
}));
disposables.add(DOM.addDisposableListener(window, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
disposables.push(DOM.addDisposableListener(window, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
if (!image) {
return;
}
@@ -463,7 +463,7 @@ class InlineImageView {
}
}));
disposables.add(DOM.addDisposableListener(container, DOM.EventType.CLICK, (e: MouseEvent) => {
disposables.push(DOM.addDisposableListener(container, DOM.EventType.CLICK, (e: MouseEvent) => {
if (!image) {
return;
}
@@ -496,7 +496,7 @@ class InlineImageView {
}
}));
disposables.add(DOM.addDisposableListener(container, DOM.EventType.WHEEL, (e: WheelEvent) => {
disposables.push(DOM.addDisposableListener(container, DOM.EventType.WHEEL, (e: WheelEvent) => {
if (!image) {
return;
}
@@ -518,7 +518,7 @@ class InlineImageView {
updateScale(scale as number * (1 - delta * InlineImageView.SCALE_PINCH_FACTOR));
}));
disposables.add(DOM.addDisposableListener(container, DOM.EventType.SCROLL, () => {
disposables.push(DOM.addDisposableListener(container, DOM.EventType.SCROLL, () => {
if (!image || !image.parentElement || scale === 'fit') {
return;
}
@@ -536,7 +536,7 @@ class InlineImageView {
image = DOM.append(container, DOM.$<HTMLImageElement>('img.scale-to-fit'));
image.style.visibility = 'hidden';
disposables.add(DOM.addDisposableListener(image, DOM.EventType.LOAD, e => {
disposables.push(DOM.addDisposableListener(image, DOM.EventType.LOAD, e => {
if (!image) {
return;
}
@@ -557,29 +557,25 @@ class InlineImageView {
}
}));
InlineImageView.imageSrc(descriptor, fileService).then(src => {
const img = container.querySelector('img');
if (img) {
if (typeof src === 'string') {
img.src = src;
} else {
const url = URL.createObjectURL(src);
disposables.add(toDisposable(() => URL.revokeObjectURL(url)));
img.src = url;
}
InlineImageView.imageSrc(descriptor, textFileService).then(dataUri => {
const imgs = container.getElementsByTagName('img');
if (imgs.length) {
imgs[0].src = dataUri;
}
});
return context;
}
private static async imageSrc(descriptor: IResourceDescriptor, fileService: IFileService): Promise<string | Blob> {
private static async imageSrc(descriptor: IResourceDescriptor, textFileService: ITextFileService): Promise<string> {
if (descriptor.resource.scheme === Schemas.data) {
return descriptor.resource.toString(true /* skip encoding */);
return Promise.resolve(descriptor.resource.toString(true /* skip encoding */));
}
const { value } = await fileService.readFile(descriptor.resource);
return new Blob([value.buffer], { type: getMime(descriptor) });
const data = await textFileService.read(descriptor.resource, { encoding: 'base64' });
const mime = getMime(descriptor);
return `data:${mime};base64,${data.value}`;
}
}

View File

@@ -169,7 +169,7 @@ export class SideBySideEditor extends BaseEditor {
}
if (!this.detailsEditor || !this.masterEditor) {
return;
return Promise.resolve();
}
await Promise.all([
@@ -213,6 +213,8 @@ export class SideBySideEditor extends BaseEditor {
this.detailsEditor.setInput(detailsInput, null, token),
this.masterEditor.setInput(masterInput, options, token)]
);
return this.focus();
}
updateStyles(): void {

View File

@@ -20,12 +20,13 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IMenuService } from 'vs/platform/actions/common/actions';
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IDisposable, dispose, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { getOrSet } from 'vs/base/common/map';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; // {{SQL CARBON EDIT}} -- Display the editor's tab color
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BACKGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, WORKBENCH_BACKGROUND, TAB_ACTIVE_BORDER_TOP, TAB_UNFOCUSED_ACTIVE_BORDER_TOP, TAB_ACTIVE_MODIFIED_BORDER, TAB_INACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER } from 'vs/workbench/common/theme';
// {{SQL CARBON EDIT}} -- Display the editor's tab color
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, WORKBENCH_BACKGROUND, TAB_ACTIVE_BORDER_TOP, TAB_UNFOCUSED_ACTIVE_BORDER_TOP, TAB_ACTIVE_MODIFIED_BORDER, TAB_INACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER } from 'vs/workbench/common/theme';
import { activeContrastBorder, contrastBorder, editorBackground, breadcrumbsBackground } from 'vs/platform/theme/common/colorRegistry';
import { ResourcesDropHandler, fillResourceDataTransfers, DraggedEditorIdentifier, DraggedEditorGroupIdentifier, DragAndDropObserver } from 'vs/workbench/browser/dnd';
import { Color } from 'vs/base/common/color';
@@ -117,7 +118,7 @@ export class TabsTitleControl extends TitleControl {
this.registerTabsContainerListeners();
// Tabs Scrollbar
this.tabsScrollbar = this._register(this.createTabsScrollbar(this.tabsContainer));
this.tabsScrollbar = this.createTabsScrollbar(this.tabsContainer);
tabsAndActionsContainer.appendChild(this.tabsScrollbar.getDomNode());
// Editor Toolbar Container
@@ -464,13 +465,13 @@ export class TabsTitleControl extends TitleControl {
// Eventing
const eventsDisposable = this.registerTabListeners(tabContainer, index);
this.tabDisposeables.push(combinedDisposable(eventsDisposable, tabActionBar, tabActionRunner, editorLabel));
this.tabDisposeables.push(combinedDisposable([eventsDisposable, tabActionBar, tabActionRunner, editorLabel]));
return tabContainer;
}
private registerTabListeners(tab: HTMLElement, index: number): IDisposable {
const disposables = new DisposableStore();
const disposables: IDisposable[] = [];
const handleClickOrTouch = (e: MouseEvent | GestureEvent): void => {
tab.blur();
@@ -506,16 +507,16 @@ export class TabsTitleControl extends TitleControl {
};
// 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)));
disposables.push(addDisposableListener(tab, EventType.MOUSE_DOWN, (e: MouseEvent) => handleClickOrTouch(e)));
disposables.push(addDisposableListener(tab, TouchEventType.Tap, (e: GestureEvent) => handleClickOrTouch(e)));
// Touch Scroll Support
disposables.add(addDisposableListener(tab, TouchEventType.Change, (e: GestureEvent) => {
disposables.push(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) => {
disposables.push(addDisposableListener(tab, EventType.MOUSE_UP, (e: MouseEvent) => {
EventHelper.stop(e);
tab.blur();
@@ -529,7 +530,7 @@ export class TabsTitleControl extends TitleControl {
}));
// Context menu on Shift+F10
disposables.add(addDisposableListener(tab, EventType.KEY_DOWN, (e: KeyboardEvent) => {
disposables.push(addDisposableListener(tab, EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
if (event.shiftKey && event.keyCode === KeyCode.F10) {
showContextMenu(e);
@@ -537,12 +538,12 @@ export class TabsTitleControl extends TitleControl {
}));
// Context menu on touch context menu gesture
disposables.add(addDisposableListener(tab, TouchEventType.Contextmenu, (e: GestureEvent) => {
disposables.push(addDisposableListener(tab, TouchEventType.Contextmenu, (e: GestureEvent) => {
showContextMenu(e);
}));
// Keyboard accessibility
disposables.add(addDisposableListener(tab, EventType.KEY_UP, (e: KeyboardEvent) => {
disposables.push(addDisposableListener(tab, EventType.KEY_UP, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
let handled = false;
@@ -587,14 +588,14 @@ export class TabsTitleControl extends TitleControl {
}));
// Pin on double click
disposables.add(addDisposableListener(tab, EventType.DBLCLICK, (e: MouseEvent) => {
disposables.push(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) => {
disposables.push(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => {
EventHelper.stop(e, true);
const input = this.group.getEditor(index);
@@ -604,7 +605,7 @@ export class TabsTitleControl extends TitleControl {
}, true /* use capture to fix https://github.com/Microsoft/vscode/issues/19145 */));
// Drag support
disposables.add(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => {
disposables.push(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => {
const editor = this.group.getEditor(index);
if (!editor) {
return;
@@ -626,7 +627,7 @@ export class TabsTitleControl extends TitleControl {
}));
// Drop support
disposables.add(new DragAndDropObserver(tab, {
disposables.push(new DragAndDropObserver(tab, {
onDragEnter: e => {
// Update class to signal drag operation
@@ -679,7 +680,7 @@ export class TabsTitleControl extends TitleControl {
}
}));
return disposables;
return combinedDisposable(disposables);
}
private isSupportedDropTransfer(e: DragEvent): boolean {
@@ -720,10 +721,10 @@ export class TabsTitleControl extends TitleControl {
element.style.outlineColor = activeContrastBorderColor;
element.style.outlineOffset = isTab ? '-5px' : '-3px';
} else {
element.style.outlineWidth = '';
element.style.outlineStyle = '';
element.style.outlineColor = activeContrastBorderColor || '';
element.style.outlineOffset = '';
element.style.outlineWidth = null;
element.style.outlineStyle = null;
element.style.outlineColor = activeContrastBorderColor;
element.style.outlineOffset = null;
}
// {{SQL CARBON EDIT}} -- Display the editor's tab color
@@ -871,7 +872,7 @@ export class TabsTitleControl extends TitleControl {
// Borders / Outline
const borderRightColor = (this.getColor(TAB_BORDER) || this.getColor(contrastBorder));
tabContainer.style.borderRight = borderRightColor ? `1px solid ${borderRightColor}` : null;
tabContainer.style.outlineColor = this.getColor(activeContrastBorder) || '';
tabContainer.style.outlineColor = this.getColor(activeContrastBorder);
// Settings
const options = this.accessor.partOptions;
@@ -929,7 +930,7 @@ export class TabsTitleControl extends TitleControl {
// Container
addClass(tabContainer, 'active');
tabContainer.setAttribute('aria-selected', 'true');
tabContainer.style.backgroundColor = this.getColor(isGroupActive ? TAB_ACTIVE_BACKGROUND : TAB_UNFOCUSED_ACTIVE_BACKGROUND);
tabContainer.style.backgroundColor = this.getColor(TAB_ACTIVE_BACKGROUND);
const activeTabBorderColorBottom = this.getColor(isGroupActive ? TAB_ACTIVE_BORDER : TAB_UNFOCUSED_ACTIVE_BORDER);
if (activeTabBorderColorBottom) {
@@ -1295,29 +1296,35 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
adjustedTabDragBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorDragAndDropBackground, editorBackgroundColor, workbenchBackground);
}
// Adjust gradient for focused and unfocused hover background
const makeTabHoverBackgroundRule = (color: Color, colorDrag: Color, hasFocus = false) => `
.monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container${hasFocus ? '.active' : ''} > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
background: linear-gradient(to left, ${color}, transparent) !important;
}
.monaco-workbench .part.editor > .content.dragged-over .editor-group-container${hasFocus ? '.active' : ''} > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
background: linear-gradient(to left, ${colorDrag}, transparent) !important;
}
`;
// Adjust gradient for (focused) hover background
if (tabHoverBackground && adjustedTabBackground && adjustedTabDragBackground) {
const adjustedColor = tabHoverBackground.flatten(adjustedTabBackground);
const adjustedColorDrag = tabHoverBackground.flatten(adjustedTabDragBackground);
collector.addRule(makeTabHoverBackgroundRule(adjustedColor, adjustedColorDrag, true));
collector.addRule(`
.monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
background: linear-gradient(to left, ${adjustedColor}, transparent) !important;
}
.monaco-workbench .part.editor > .content.dragged-over .editor-group-container.active > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important;
}
`);
}
// Adjust gradient for unfocused hover background
if (tabUnfocusedHoverBackground && adjustedTabBackground && adjustedTabDragBackground) {
const adjustedColor = tabUnfocusedHoverBackground.flatten(adjustedTabBackground);
const adjustedColorDrag = tabUnfocusedHoverBackground.flatten(adjustedTabDragBackground);
collector.addRule(makeTabHoverBackgroundRule(adjustedColor, adjustedColorDrag));
collector.addRule(`
.monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
background: linear-gradient(to left, ${adjustedColor}, transparent) !important;
}
.monaco-workbench .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.dragged):hover > .tab-label::after {
background: linear-gradient(to left, ${adjustedColorDrag}, transparent) !important;
}
`);
}
// Adjust gradient for drag and drop background
@@ -1331,31 +1338,20 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
`);
}
// Adjust gradient for active tab background (focused and unfocused editor groups)
const makeTabActiveBackgroundRule = (color: Color, colorDrag: Color, hasFocus = false) => `
.monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container${hasFocus ? '.active' : ':not(.active)'} > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after {
background: linear-gradient(to left, ${color}, transparent);
}
.monaco-workbench .part.editor > .content.dragged-over .editor-group-container${hasFocus ? '.active' : ':not(.active)'} > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after {
background: linear-gradient(to left, ${colorDrag}, transparent);
}
`;
// Adjust gradient for unfocused active tab background
// Adjust gradient for active tab background
const tabActiveBackground = theme.getColor(TAB_ACTIVE_BACKGROUND);
if (tabActiveBackground && adjustedTabBackground && adjustedTabDragBackground) {
const adjustedColor = tabActiveBackground.flatten(adjustedTabBackground);
const adjustedColorDrag = tabActiveBackground.flatten(adjustedTabDragBackground);
collector.addRule(makeTabActiveBackgroundRule(adjustedColor, adjustedColorDrag, true));
}
collector.addRule(`
.monaco-workbench .part.editor > .content:not(.dragged-over) .editor-group-container > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after {
background: linear-gradient(to left, ${adjustedColor}, transparent);
}
// Adjust gradient for unfocused active tab background
const tabUnfocusedActiveBackground = theme.getColor(TAB_UNFOCUSED_ACTIVE_BACKGROUND);
if (tabUnfocusedActiveBackground && adjustedTabBackground && adjustedTabDragBackground) {
const adjustedColor = tabUnfocusedActiveBackground.flatten(adjustedTabBackground);
const adjustedColorDrag = tabUnfocusedActiveBackground.flatten(adjustedTabDragBackground);
collector.addRule(makeTabActiveBackgroundRule(adjustedColor, adjustedColorDrag));
.monaco-workbench .part.editor > .content.dragged-over .editor-group-container > .title .tabs-container > .tab.sizing-shrink.active:not(.dragged) > .tab-label::after {
background: linear-gradient(to left, ${adjustedColorDrag}, transparent);
}
`);
}
// Adjust gradient for inactive tab background

View File

@@ -70,7 +70,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
// Assert Model instance
if (!(resolvedModel instanceof BaseTextEditorModel)) {
throw new Error('Unable to open file as text');
return Promise.reject(new Error('Unable to open file as text'));
}
// Set Editor Model

View File

@@ -18,7 +18,7 @@ import { localize } from 'vs/nls';
import { createActionViewItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { ExecuteCommandAction, IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -33,7 +33,7 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource, IEditorPartOptions, SideBySideEditor, EditorPinnedContext } from 'vs/workbench/common/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource, IEditorPartOptions, SideBySideEditor } from 'vs/workbench/common/editor';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { Themable } from 'vs/workbench/common/theme';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -59,8 +59,6 @@ export abstract class TitleControl extends Themable {
protected editorActionsToolbar: ToolBar;
private resourceContext: ResourceContextKey;
private editorPinnedContext: IContextKey<boolean>;
private editorToolBarMenuDisposables: IDisposable[] = [];
private contextMenu: IMenu;
@@ -87,8 +85,6 @@ export abstract class TitleControl extends Themable {
super(themeService);
this.resourceContext = this._register(instantiationService.createInstance(ResourceContextKey));
this.editorPinnedContext = EditorPinnedContext.bindTo(contextKeyService);
this.contextMenu = this._register(this.menuService.createMenu(MenuId.EditorTitleContext, this.contextKeyService));
this.create(parent);
@@ -225,9 +221,8 @@ export abstract class TitleControl extends Themable {
// Dispose previous listeners
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
// Update contexts
// Update the resource context
this.resourceContext.set(this.group.activeEditor ? withUndefinedAsNull(toResource(this.group.activeEditor, { supportSideBySide: SideBySideEditor.MASTER })) : null);
this.editorPinnedContext.set(this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false);
// Editor actions require the editor control to be there, so we retrieve it via service
const activeControl = this.group.activeControl;
@@ -292,11 +287,9 @@ export abstract class TitleControl extends Themable {
protected onContextMenu(editor: IEditorInput, e: Event, node: HTMLElement): void {
// Update contexts based on editor picked and remember previous to restore
const currentResourceContext = this.resourceContext.get();
// Update the resource context
const currentContext = this.resourceContext.get();
this.resourceContext.set(withUndefinedAsNull(toResource(editor, { supportSideBySide: SideBySideEditor.MASTER })));
const currentPinnedContext = !!this.editorPinnedContext.get();
this.editorPinnedContext.set(this.group.isPinned(editor));
// Find target anchor
let anchor: HTMLElement | { x: number, y: number } = node;
@@ -317,9 +310,8 @@ export abstract class TitleControl extends Themable {
getKeyBinding: (action) => this.getKeybinding(action),
onHide: () => {
// restore previous contexts
this.resourceContext.set(currentResourceContext || null);
this.editorPinnedContext.set(currentPinnedContext);
// restore previous context
this.resourceContext.set(currentContext || null);
// restore focus to active group
this.accessor.activeGroup.focus();

View File

@@ -121,12 +121,12 @@ export class ConfigureNotificationAction extends Action {
constructor(
id: string,
label: string,
private readonly _configurationActions: ReadonlyArray<IAction>
private _configurationActions: IAction[]
) {
super(id, label, 'configure-notification-action');
}
get configurationActions(): ReadonlyArray<IAction> {
get configurationActions(): IAction[] {
return this._configurationActions;
}
}

View File

@@ -219,7 +219,7 @@ export class NotificationsList extends Themable {
this.listContainer.style.background = background ? background.toString() : null;
const outlineColor = this.getColor(contrastBorder);
this.listContainer.style.outlineColor = outlineColor ? outlineColor.toString() : '';
this.listContainer.style.outlineColor = outlineColor ? outlineColor.toString() : null;
}
}

View File

@@ -3,20 +3,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INotificationsModel, INotificationChangeEvent, NotificationChangeType, INotificationViewItem, IStatusMessageChangeEvent, StatusMessageChangeType, IStatusMessageViewItem } from 'vs/workbench/common/notifications';
import { INotificationsModel, INotificationChangeEvent, NotificationChangeType, INotificationViewItem } from 'vs/workbench/common/notifications';
import { IStatusbarService, StatusbarAlignment, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { HIDE_NOTIFICATIONS_CENTER, SHOW_NOTIFICATIONS_CENTER } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { localize } from 'vs/nls';
export class NotificationsStatus extends Disposable {
private notificationsCenterStatusItem: IStatusbarEntryAccessor;
private currentNotifications = new Set<INotificationViewItem>();
private currentStatusMessage: [IStatusMessageViewItem, IDisposable] | undefined;
private statusItem: IStatusbarEntryAccessor;
private isNotificationsCenterVisible: boolean;
private _counter: Set<INotificationViewItem>;
constructor(
private model: INotificationsModel,
@@ -24,18 +20,29 @@ export class NotificationsStatus extends Disposable {
) {
super();
this.updateNotificationsCenterStatusItem();
this._counter = new Set<INotificationViewItem>();
if (model.statusMessage) {
this.doSetStatusMessage(model.statusMessage);
}
this.updateNotificationsStatusItem();
this.registerListeners();
}
private get count(): number {
return this._counter.size;
}
update(isCenterVisible: boolean): void {
if (this.isNotificationsCenterVisible !== isCenterVisible) {
this.isNotificationsCenterVisible = isCenterVisible;
// Showing the notification center resets the counter to 0
this._counter.clear();
this.updateNotificationsStatusItem();
}
}
private registerListeners(): void {
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
this._register(this.model.onDidStatusMessageChange(e => this.onDidStatusMessageChange(e)));
}
private onDidNotificationChange(e: INotificationChangeEvent): void {
@@ -45,29 +52,29 @@ export class NotificationsStatus extends Disposable {
// Notification got Added
if (e.kind === NotificationChangeType.ADD) {
this.currentNotifications.add(e.item);
this._counter.add(e.item);
}
// Notification got Removed
else if (e.kind === NotificationChangeType.REMOVE) {
this.currentNotifications.delete(e.item);
this._counter.delete(e.item);
}
this.updateNotificationsCenterStatusItem();
this.updateNotificationsStatusItem();
}
private updateNotificationsCenterStatusItem(): void {
private updateNotificationsStatusItem(): void {
const statusProperties: IStatusbarEntry = {
text: this.currentNotifications.size === 0 ? '$(bell)' : `$(bell) ${this.currentNotifications.size}`,
text: this.count === 0 ? '$(bell)' : `$(bell) ${this.count}`,
command: this.isNotificationsCenterVisible ? HIDE_NOTIFICATIONS_CENTER : SHOW_NOTIFICATIONS_CENTER,
tooltip: this.getTooltip(),
showBeak: this.isNotificationsCenterVisible
};
if (!this.notificationsCenterStatusItem) {
this.notificationsCenterStatusItem = this.statusbarService.addEntry(statusProperties, 'status.notifications', localize('status.notifications', "Notifications"), StatusbarAlignment.RIGHT, -1000 /* towards the far end of the right hand side */);
if (!this.statusItem) {
this.statusItem = this.statusbarService.addEntry(statusProperties, StatusbarAlignment.RIGHT, -1000 /* towards the far end of the right hand side */);
} else {
this.notificationsCenterStatusItem.update(statusProperties);
this.statusItem.update(statusProperties);
}
}
@@ -80,96 +87,14 @@ export class NotificationsStatus extends Disposable {
return localize('zeroNotifications', "No Notifications");
}
if (this.currentNotifications.size === 0) {
if (this.count === 0) {
return localize('noNotifications', "No New Notifications");
}
if (this.currentNotifications.size === 1) {
if (this.count === 1) {
return localize('oneNotification', "1 New Notification");
}
return localize('notifications', "{0} New Notifications", this.currentNotifications.size);
}
update(isCenterVisible: boolean): void {
if (this.isNotificationsCenterVisible !== isCenterVisible) {
this.isNotificationsCenterVisible = isCenterVisible;
// Showing the notification center resets the counter to 0
this.currentNotifications.clear();
this.updateNotificationsCenterStatusItem();
}
}
private onDidStatusMessageChange(e: IStatusMessageChangeEvent): void {
const statusItem = e.item;
switch (e.kind) {
// Show status notification
case StatusMessageChangeType.ADD:
this.doSetStatusMessage(statusItem);
break;
// Hide status notification (if its still the current one)
case StatusMessageChangeType.REMOVE:
if (this.currentStatusMessage && this.currentStatusMessage[0] === statusItem) {
dispose(this.currentStatusMessage[1]);
this.currentStatusMessage = undefined;
}
break;
}
}
private doSetStatusMessage(item: IStatusMessageViewItem): void {
const message = item.message;
const showAfter = item.options && typeof item.options.showAfter === 'number' ? item.options.showAfter : 0;
const hideAfter = item.options && typeof item.options.hideAfter === 'number' ? item.options.hideAfter : -1;
// Dismiss any previous
if (this.currentStatusMessage) {
dispose(this.currentStatusMessage[1]);
}
// Create new
let statusMessageEntry: IStatusbarEntryAccessor;
let showHandle: any = setTimeout(() => {
statusMessageEntry = this.statusbarService.addEntry(
{ text: message },
'status.message',
localize('status.message', "Status Message"),
StatusbarAlignment.LEFT,
-Number.MAX_VALUE /* far right on left hand side */
);
showHandle = null;
}, showAfter);
// Dispose function takes care of timeouts and actual entry
let hideHandle: any;
const statusMessageDispose = {
dispose: () => {
if (showHandle) {
clearTimeout(showHandle);
}
if (hideHandle) {
clearTimeout(hideHandle);
}
if (statusMessageEntry) {
statusMessageEntry.dispose();
}
}
};
if (hideAfter > 0) {
hideHandle = setTimeout(() => statusMessageDispose.dispose(), hideAfter);
}
// Remember as current status message
this.currentStatusMessage = [item, statusMessageDispose];
return localize('notifications', "{0} New Notifications", this.count);
}
}

View File

@@ -16,7 +16,6 @@ import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/
import { ActivityAction } from 'vs/workbench/browser/parts/compositeBarActions';
import { IActivity } from 'vs/workbench/common/activity';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ActivePanelContext, PanelPositionContext } from 'vs/workbench/common/panel';
export class ClosePanelAction extends Action {
@@ -93,8 +92,8 @@ export class TogglePanelPositionAction extends Action {
static readonly ID = 'workbench.action.togglePanelPosition';
static readonly LABEL = nls.localize('toggledPanelPosition', "Toggle Panel Position");
static readonly MOVE_TO_RIGHT_LABEL = nls.localize('moveToRight', "Move Panel Right");
static readonly MOVE_TO_BOTTOM_LABEL = nls.localize('moveToBottom', "Move Panel to Bottom");
private static readonly MOVE_TO_RIGHT_LABEL = nls.localize('moveToRight', "Move Panel Right");
private static readonly MOVE_TO_BOTTOM_LABEL = nls.localize('moveToBottom', "Move Panel to Bottom");
private toDispose: IDisposable[];
@@ -272,28 +271,16 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '2_workbench_layout',
command: {
id: TogglePanelAction.ID,
title: nls.localize({ key: 'miShowPanel', comment: ['&& denotes a mnemonic'] }, "Show &&Panel"),
toggled: ActivePanelContext
title: nls.localize({ key: 'miTogglePanel', comment: ['&& denotes a mnemonic'] }, "Toggle &&Panel")
},
order: 5
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '3_workbench_layout_move',
group: '2_workbench_layout',
command: {
id: TogglePanelPositionAction.ID,
title: TogglePanelPositionAction.MOVE_TO_RIGHT_LABEL
title: TogglePanelPositionAction.LABEL
},
when: PanelPositionContext.isEqualTo('bottom'),
order: 5
});
MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, {
group: '3_workbench_layout_move',
command: {
id: TogglePanelPositionAction.ID,
title: TogglePanelPositionAction.MOVE_TO_BOTTOM_LABEL
},
when: PanelPositionContext.isEqualTo('right'),
order: 5
order: 3
});

View File

@@ -70,7 +70,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
private panelFocusContextKey: IContextKey<boolean>;
private compositeBar: CompositeBar;
private compositeActions: Map<string, { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction }> = new Map();
private compositeActions: { [compositeId: string]: { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null);
private blockOpeningPanel: boolean;
private dimension: Dimension;
@@ -245,7 +245,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
.sort((p1, p2) => pinnedCompositeIds.indexOf(p1.id) - pinnedCompositeIds.indexOf(p2.id));
}
protected getActions(): ReadonlyArray<IAction> {
protected getActions(): IAction[] {
return [
this.instantiationService.createInstance(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL),
this.instantiationService.createInstance(ClosePanelAction, ClosePanelAction.ID, ClosePanelAction.LABEL)
@@ -261,11 +261,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
}
hideActivePanel(): void {
// First check if panel is visible and hide if so
if (this.layoutService.isVisible(Parts.PANEL_PART)) {
this.layoutService.setPanelHidden(true);
}
this.hideActiveComposite();
}
@@ -316,14 +311,13 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
}
private getCompositeActions(compositeId: string): { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } {
let compositeActions = this.compositeActions.get(compositeId);
let compositeActions = this.compositeActions[compositeId];
if (!compositeActions) {
compositeActions = {
activityAction: this.instantiationService.createInstance(PanelActivityAction, this.getPanel(compositeId)),
pinnedAction: new ToggleCompositePinnedAction(this.getPanel(compositeId), this.compositeBar)
};
this.compositeActions.set(compositeId, compositeActions);
this.compositeActions[compositeId] = compositeActions;
}
return compositeActions;
@@ -331,11 +325,11 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
protected removeComposite(compositeId: string): boolean {
if (super.removeComposite(compositeId)) {
const compositeActions = this.compositeActions.get(compositeId);
const compositeActions = this.compositeActions[compositeId];
if (compositeActions) {
compositeActions.activityAction.dispose();
compositeActions.pinnedAction.dispose();
this.compositeActions.delete(compositeId);
delete this.compositeActions[compositeId];
}
return true;

View File

@@ -27,7 +27,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { Emitter, Event } from 'vs/base/common/event';
import { Button } from 'vs/base/browser/ui/button/button';
import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
@@ -97,7 +97,7 @@ type Visibilities = {
customButton?: boolean;
};
class QuickInput extends Disposable implements IQuickInput {
class QuickInput implements IQuickInput {
private _title: string;
private _steps: number;
@@ -109,17 +109,18 @@ class QuickInput extends Disposable implements IQuickInput {
private _ignoreFocusOut = false;
private _buttons: IQuickInputButton[] = [];
private buttonsUpdated = false;
private readonly onDidTriggerButtonEmitter = this._register(new Emitter<IQuickInputButton>());
private readonly onDidHideEmitter = this._register(new Emitter<void>());
private onDidTriggerButtonEmitter = new Emitter<IQuickInputButton>();
private onDidHideEmitter = new Emitter<void>();
protected readonly visibleDisposables = this._register(new DisposableStore());
protected visibleDisposables: IDisposable[] = [];
protected disposables: IDisposable[] = [
this.onDidTriggerButtonEmitter,
this.onDidHideEmitter,
];
private busyDelay: TimeoutTimer | null;
constructor(
protected ui: QuickInputUI
) {
super();
constructor(protected ui: QuickInputUI) {
}
get title() {
@@ -201,7 +202,7 @@ class QuickInput extends Disposable implements IQuickInput {
if (this.visible) {
return;
}
this.visibleDisposables.add(
this.visibleDisposables.push(
this.ui.onDidTriggerButton(button => {
if (this.buttons.indexOf(button) !== -1) {
this.onDidTriggerButtonEmitter.fire(button);
@@ -222,7 +223,7 @@ class QuickInput extends Disposable implements IQuickInput {
didHide(): void {
this.visible = false;
this.visibleDisposables.clear();
this.visibleDisposables = dispose(this.visibleDisposables);
this.onDidHideEmitter.fire();
}
@@ -316,7 +317,7 @@ class QuickInput extends Disposable implements IQuickInput {
public dispose(): void {
this.hide();
super.dispose();
this.disposables = dispose(this.disposables);
}
}
@@ -326,9 +327,9 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
private _value = '';
private _placeholder: string;
private readonly onDidChangeValueEmitter = this._register(new Emitter<string>());
private readonly onDidAcceptEmitter = this._register(new Emitter<void>());
private readonly onDidCustomEmitter = this._register(new Emitter<void>());
private onDidChangeValueEmitter = new Emitter<string>();
private onDidAcceptEmitter = new Emitter<void>();
private onDidCustomEmitter = new Emitter<void>();
private _items: Array<T | IQuickPickSeparator> = [];
private itemsUpdated = false;
private _canSelectMany = false;
@@ -339,12 +340,12 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
private _activeItems: T[] = [];
private activeItemsUpdated = false;
private activeItemsToConfirm: T[] | null = [];
private readonly onDidChangeActiveEmitter = this._register(new Emitter<T[]>());
private onDidChangeActiveEmitter = new Emitter<T[]>();
private _selectedItems: T[] = [];
private selectedItemsUpdated = false;
private selectedItemsToConfirm: T[] | null = [];
private readonly onDidChangeSelectionEmitter = this._register(new Emitter<T[]>());
private readonly onDidTriggerItemButtonEmitter = this._register(new Emitter<IQuickPickItemButtonEvent<T>>());
private onDidChangeSelectionEmitter = new Emitter<T[]>();
private onDidTriggerItemButtonEmitter = new Emitter<IQuickPickItemButtonEvent<T>>();
private _valueSelection: Readonly<[number, number]>;
private valueSelectionUpdated = true;
private _validationMessage: string;
@@ -355,6 +356,17 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
quickNavigate: IQuickNavigateConfiguration;
constructor(ui: QuickInputUI) {
super(ui);
this.disposables.push(
this.onDidChangeValueEmitter,
this.onDidAcceptEmitter,
this.onDidCustomEmitter,
this.onDidChangeActiveEmitter,
this.onDidChangeSelectionEmitter,
this.onDidTriggerItemButtonEmitter,
);
}
get value() {
return this._value;
@@ -530,7 +542,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
show() {
if (!this.visible) {
this.visibleDisposables.add(
this.visibleDisposables.push(
this.ui.inputBox.onDidChange(value => {
if (value === this.value) {
return;
@@ -539,104 +551,105 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
this.ui.list.filter(this.ui.inputBox.value);
this.trySelectFirst();
this.onDidChangeValueEmitter.fire(value);
}));
this.visibleDisposables.add(this.ui.inputBox.onMouseDown(event => {
if (!this.autoFocusOnList) {
this.ui.list.clearFocus();
}
}));
this.visibleDisposables.add(this.ui.inputBox.onKeyDown(event => {
switch (event.keyCode) {
case KeyCode.DownArrow:
this.ui.list.focus('Next');
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
break;
case KeyCode.UpArrow:
if (this.ui.list.getFocusedElements().length) {
this.ui.list.focus('Previous');
} else {
this.ui.list.focus('Last');
}
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
break;
case KeyCode.PageDown:
if (this.ui.list.getFocusedElements().length) {
this.ui.list.focus('NextPage');
} else {
this.ui.list.focus('First');
}
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
break;
case KeyCode.PageUp:
if (this.ui.list.getFocusedElements().length) {
this.ui.list.focus('PreviousPage');
} else {
this.ui.list.focus('Last');
}
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
break;
}
}));
this.visibleDisposables.add(this.ui.onDidAccept(() => {
if (!this.canSelectMany && this.activeItems[0]) {
this._selectedItems = [this.activeItems[0]];
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
}
this.onDidAcceptEmitter.fire(undefined);
}));
this.visibleDisposables.add(this.ui.onDidCustom(() => {
this.onDidCustomEmitter.fire(undefined);
}));
this.visibleDisposables.add(this.ui.list.onDidChangeFocus(focusedItems => {
if (this.activeItemsUpdated) {
return; // Expect another event.
}
if (this.activeItemsToConfirm !== this._activeItems && equals(focusedItems, this._activeItems, (a, b) => a === b)) {
return;
}
this._activeItems = focusedItems as T[];
this.onDidChangeActiveEmitter.fire(focusedItems as T[]);
}));
this.visibleDisposables.add(this.ui.list.onDidChangeSelection(selectedItems => {
if (this.canSelectMany) {
if (selectedItems.length) {
this.ui.list.setSelectedElements([]);
}),
this.ui.inputBox.onMouseDown(event => {
if (!this.autoFocusOnList) {
this.ui.list.clearFocus();
}
}),
this.ui.inputBox.onKeyDown(event => {
switch (event.keyCode) {
case KeyCode.DownArrow:
this.ui.list.focus('Next');
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
break;
case KeyCode.UpArrow:
if (this.ui.list.getFocusedElements().length) {
this.ui.list.focus('Previous');
} else {
this.ui.list.focus('Last');
}
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
break;
case KeyCode.PageDown:
if (this.ui.list.getFocusedElements().length) {
this.ui.list.focus('NextPage');
} else {
this.ui.list.focus('First');
}
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
break;
case KeyCode.PageUp:
if (this.ui.list.getFocusedElements().length) {
this.ui.list.focus('PreviousPage');
} else {
this.ui.list.focus('Last');
}
if (this.canSelectMany) {
this.ui.list.domFocus();
}
event.preventDefault();
break;
}
}),
this.ui.onDidAccept(() => {
if (!this.canSelectMany && this.activeItems[0]) {
this._selectedItems = [this.activeItems[0]];
this.onDidChangeSelectionEmitter.fire(this.selectedItems);
}
return;
}
if (this.selectedItemsToConfirm !== this._selectedItems && equals(selectedItems, this._selectedItems, (a, b) => a === b)) {
return;
}
this._selectedItems = selectedItems as T[];
this.onDidChangeSelectionEmitter.fire(selectedItems as T[]);
if (selectedItems.length) {
this.onDidAcceptEmitter.fire(undefined);
}
}));
this.visibleDisposables.add(this.ui.list.onChangedCheckedElements(checkedItems => {
if (!this.canSelectMany) {
return;
}
if (this.selectedItemsToConfirm !== this._selectedItems && equals(checkedItems, this._selectedItems, (a, b) => a === b)) {
return;
}
this._selectedItems = checkedItems as T[];
this.onDidChangeSelectionEmitter.fire(checkedItems as T[]);
}));
this.visibleDisposables.add(this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event as IQuickPickItemButtonEvent<T>)));
this.visibleDisposables.add(this.registerQuickNavigation());
}),
this.ui.onDidCustom(() => {
this.onDidCustomEmitter.fire(undefined);
}),
this.ui.list.onDidChangeFocus(focusedItems => {
if (this.activeItemsUpdated) {
return; // Expect another event.
}
if (this.activeItemsToConfirm !== this._activeItems && equals(focusedItems, this._activeItems, (a, b) => a === b)) {
return;
}
this._activeItems = focusedItems as T[];
this.onDidChangeActiveEmitter.fire(focusedItems as T[]);
}),
this.ui.list.onDidChangeSelection(selectedItems => {
if (this.canSelectMany) {
if (selectedItems.length) {
this.ui.list.setSelectedElements([]);
}
return;
}
if (this.selectedItemsToConfirm !== this._selectedItems && equals(selectedItems, this._selectedItems, (a, b) => a === b)) {
return;
}
this._selectedItems = selectedItems as T[];
this.onDidChangeSelectionEmitter.fire(selectedItems as T[]);
if (selectedItems.length) {
this.onDidAcceptEmitter.fire(undefined);
}
}),
this.ui.list.onChangedCheckedElements(checkedItems => {
if (!this.canSelectMany) {
return;
}
if (this.selectedItemsToConfirm !== this._selectedItems && equals(checkedItems, this._selectedItems, (a, b) => a === b)) {
return;
}
this._selectedItems = checkedItems as T[];
this.onDidChangeSelectionEmitter.fire(checkedItems as T[]);
}),
this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event as IQuickPickItemButtonEvent<T>)),
this.registerQuickNavigation()
);
this.valueSelectionUpdated = true;
}
super.show(); // TODO: Why have show() bubble up while update() trickles down? (Could move setComboboxAccessibility() here.)
@@ -771,8 +784,16 @@ class InputBox extends QuickInput implements IInputBox {
private _prompt: string;
private noValidationMessage = InputBox.noPromptMessage;
private _validationMessage: string;
private readonly onDidValueChangeEmitter = this._register(new Emitter<string>());
private readonly onDidAcceptEmitter = this._register(new Emitter<void>());
private onDidValueChangeEmitter = new Emitter<string>();
private onDidAcceptEmitter = new Emitter<void>();
constructor(ui: QuickInputUI) {
super(ui);
this.disposables.push(
this.onDidValueChangeEmitter,
this.onDidAcceptEmitter,
);
}
get value() {
return this._value;
@@ -828,21 +849,22 @@ class InputBox extends QuickInput implements IInputBox {
this.update();
}
readonly onDidChangeValue = this.onDidValueChangeEmitter.event;
onDidChangeValue = this.onDidValueChangeEmitter.event;
readonly onDidAccept = this.onDidAcceptEmitter.event;
onDidAccept = this.onDidAcceptEmitter.event;
show() {
if (!this.visible) {
this.visibleDisposables.add(
this.visibleDisposables.push(
this.ui.inputBox.onDidChange(value => {
if (value === this.value) {
return;
}
this._value = value;
this.onDidValueChangeEmitter.fire(value);
}));
this.visibleDisposables.add(this.ui.onDidAccept(() => this.onDidAcceptEmitter.fire(undefined)));
}),
this.ui.onDidAccept(() => this.onDidAcceptEmitter.fire(undefined)),
);
this.valueSelectionUpdated = true;
}
super.show();
@@ -898,10 +920,10 @@ export class QuickInputService extends Component implements IQuickInputService {
private enabled = true;
private inQuickOpenWidgets: Record<string, boolean> = {};
private inQuickOpenContext: IContextKey<boolean>;
private contexts: Map<string, IContextKey<boolean>> = new Map();
private readonly onDidAcceptEmitter = this._register(new Emitter<void>());
private readonly onDidCustomEmitter = this._register(new Emitter<void>());
private readonly onDidTriggerButtonEmitter = this._register(new Emitter<IQuickInputButton>());
private contexts: { [id: string]: IContextKey<boolean>; } = Object.create(null);
private onDidAcceptEmitter = this._register(new Emitter<void>());
private onDidCustomEmitter = this._register(new Emitter<void>());
private onDidTriggerButtonEmitter = this._register(new Emitter<IQuickInputButton>());
private keyMods: Writeable<IKeyMods> = { ctrlCmd: false, alt: false };
private controller: QuickInput | null = null;
@@ -947,11 +969,11 @@ export class QuickInputService extends Component implements IQuickInputService {
private setContextKey(id?: string) {
let key: IContextKey<boolean> | undefined;
if (id) {
key = this.contexts.get(id);
key = this.contexts[id];
if (!key) {
key = new RawContextKey<boolean>(id, false)
.bindTo(this.contextKeyService);
this.contexts.set(id, key);
this.contexts[id] = key;
}
}
@@ -967,11 +989,11 @@ export class QuickInputService extends Component implements IQuickInputService {
}
private resetContextKeys() {
this.contexts.forEach(context => {
if (context.get()) {
context.reset();
for (const key in this.contexts) {
if (this.contexts[key].get()) {
this.contexts[key].reset();
}
});
}
}
private registerKeyModsListeners() {
@@ -1106,9 +1128,6 @@ export class QuickInputService extends Component implements IQuickInputService {
this.hide(true);
}
}));
this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, (e: FocusEvent) => {
inputBox.setFocus();
}));
this._register(dom.addDisposableListener(container, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
switch (event.keyCode) {

View File

@@ -8,24 +8,25 @@ import * as dom from 'vs/base/browser/dom';
import { InputBox, IRange, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { inputBackground, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoForeground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningForeground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorForeground, inputValidationErrorBorder } from 'vs/platform/theme/common/colorRegistry';
import { ITheme } from 'vs/platform/theme/common/themeService';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import Severity from 'vs/base/common/severity';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
const $ = dom.$;
export class QuickInputBox extends Disposable {
export class QuickInputBox {
private container: HTMLElement;
private inputBox: InputBox;
private disposables: IDisposable[] = [];
constructor(
private parent: HTMLElement
) {
super();
this.container = dom.append(this.parent, $('.quick-input-box'));
this.inputBox = this._register(new InputBox(this.container, undefined));
this.inputBox = new InputBox(this.container, undefined);
this.disposables.push(this.inputBox);
}
onKeyDown = (handler: (event: StandardKeyboardEvent) => void): IDisposable => {
@@ -128,4 +129,8 @@ export class QuickInputBox extends Disposable {
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder),
});
}
dispose() {
this.disposables = dispose(this.disposables);
}
}

View File

@@ -74,9 +74,9 @@ export class QuickOpenController extends Component implements IQuickOpenService
private lastInputValue: string;
private lastSubmittedInputValue: string;
private quickOpenWidget: QuickOpenWidget;
private mapResolvedHandlersToPrefix: Map<string, Promise<QuickOpenHandler>> = new Map();
private mapContextKeyToContext: Map<string, IContextKey<boolean>> = new Map();
private handlerOnOpenCalled: Set<string> = new Set();
private mapResolvedHandlersToPrefix: { [prefix: string]: Promise<QuickOpenHandler>; } = Object.create(null);
private mapContextKeyToContext: { [id: string]: IContextKey<boolean>; } = Object.create(null);
private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null);
private promisesToCompleteOnHide: ValueCallback[] = [];
private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor | null;
private actionProvider = new ContributableActionProvider();
@@ -258,13 +258,13 @@ export class QuickOpenController extends Component implements IQuickOpenService
this.cancelPendingGetResultsInvocation();
// Pass to handlers
this.mapResolvedHandlersToPrefix.forEach((promise, prefix) => {
promise.then(handler => {
this.handlerOnOpenCalled.delete(prefix);
for (let prefix in this.mapResolvedHandlersToPrefix) {
this.mapResolvedHandlersToPrefix[prefix].then(handler => {
this.handlerOnOpenCalled[prefix] = false;
handler.onClose(reason === HideReason.CANCELED); // Don't check if onOpen was called to preserve old behaviour for now
});
});
}
// Complete promises that are waiting
while (this.promisesToCompleteOnHide.length) {
@@ -294,16 +294,16 @@ export class QuickOpenController extends Component implements IQuickOpenService
}
private resetQuickOpenContextKeys(): void {
this.mapContextKeyToContext.forEach(context => context.reset());
Object.keys(this.mapContextKeyToContext).forEach(k => this.mapContextKeyToContext[k].reset());
}
private setQuickOpenContextKey(id?: string): void {
let key: IContextKey<boolean> | undefined;
if (id) {
key = this.mapContextKeyToContext.get(id);
key = this.mapContextKeyToContext[id];
if (!key) {
key = new RawContextKey<boolean>(id, false).bindTo(this.contextKeyService);
this.mapContextKeyToContext.set(id, key);
this.mapContextKeyToContext[id] = key;
}
}
@@ -454,16 +454,14 @@ export class QuickOpenController extends Component implements IQuickOpenService
const previousInput = this.quickOpenWidget.getInput();
const wasShowingHistory = previousInput && previousInput.entries && previousInput.entries.some(e => e instanceof EditorHistoryEntry || e instanceof EditorHistoryEntryGroup);
if (wasShowingHistory || matchingHistoryEntries.length > 0) {
(async () => {
if (resolvedHandler.hasShortResponseTime()) {
await timeout(QuickOpenController.MAX_SHORT_RESPONSE_TIME);
}
if (resolvedHandler.hasShortResponseTime()) {
await timeout(QuickOpenController.MAX_SHORT_RESPONSE_TIME);
}
if (!token.isCancellationRequested && !inputSet) {
this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true });
inputSet = true;
}
})();
if (!token.isCancellationRequested && !inputSet) {
this.quickOpenWidget.setInput(quickOpenModel, { autoFocusFirstEntry: true });
inputSet = true;
}
}
// Get results
@@ -525,7 +523,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(placeHolderLabel)], this.actionProvider);
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
return;
return Promise.resolve(undefined);
}
// Support extra class from handler
@@ -581,45 +579,38 @@ export class QuickOpenController extends Component implements IQuickOpenService
return mapEntryToPath;
}
private async resolveHandler(handler: QuickOpenHandlerDescriptor): Promise<QuickOpenHandler> {
let result = this.doResolveHandler(handler);
private resolveHandler(handler: QuickOpenHandlerDescriptor): Promise<QuickOpenHandler> {
let result = this._resolveHandler(handler);
const id = handler.getId();
if (!this.handlerOnOpenCalled.has(id)) {
if (!this.handlerOnOpenCalled[id]) {
const original = result;
this.handlerOnOpenCalled.add(id);
result = original.then(resolved => {
this.mapResolvedHandlersToPrefix.set(id, original);
this.handlerOnOpenCalled[id] = true;
result = this.mapResolvedHandlersToPrefix[id] = original.then(resolved => {
this.mapResolvedHandlersToPrefix[id] = original;
resolved.onOpen();
return resolved;
});
this.mapResolvedHandlersToPrefix.set(id, result);
}
try {
return await result;
} catch (error) {
this.mapResolvedHandlersToPrefix.delete(id);
return result.then<QuickOpenHandler>(null, (error) => {
delete this.mapResolvedHandlersToPrefix[id];
throw new Error(`Unable to instantiate quick open handler ${handler.getId()}: ${JSON.stringify(error)}`);
}
return Promise.reject(new Error(`Unable to instantiate quick open handler ${handler.getId()}: ${JSON.stringify(error)}`));
});
}
private doResolveHandler(handler: QuickOpenHandlerDescriptor): Promise<QuickOpenHandler> {
private _resolveHandler(handler: QuickOpenHandlerDescriptor): Promise<QuickOpenHandler> {
const id = handler.getId();
// Return Cached
if (this.mapResolvedHandlersToPrefix.has(id)) {
return this.mapResolvedHandlersToPrefix.get(id)!;
if (this.mapResolvedHandlersToPrefix[id]) {
return this.mapResolvedHandlersToPrefix[id];
}
// Otherwise load and create
const result = Promise.resolve(handler.instantiate(this.instantiationService));
this.mapResolvedHandlersToPrefix.set(id, result);
return result;
return this.mapResolvedHandlersToPrefix[id] = Promise.resolve(handler.instantiate(this.instantiationService));
}
layout(dimension: Dimension): void {

View File

@@ -191,7 +191,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements IViewletServi
async openViewlet(id: string | undefined, focus?: boolean): Promise<IViewlet | null> {
if (typeof id === 'string' && this.getViewlet(id)) {
return this.doOpenViewlet(id, focus);
return Promise.resolve(this.doOpenViewlet(id, focus));
}
await this.extensionService.whenInstalledExtensionsRegistered();

View File

@@ -9,35 +9,20 @@
width: 100%;
height: 22px;
font-size: 12px;
display: flex;
}
.monaco-workbench .part.statusbar > .left-items,
.monaco-workbench .part.statusbar > .right-items {
display: flex;
flex-wrap: wrap; /* individual entries should not shrink since we do not control their content */
}
.monaco-workbench .part.statusbar > .right-items {
flex-direction: row-reverse; /* ensures that the most right elements wrap last when space is little */
}
.monaco-workbench .part.statusbar > .left-items {
flex-grow: 1; /* left items push right items to the far right end */
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item {
.monaco-workbench .part.statusbar > .statusbar-item {
display: inline-block;
line-height: 22px;
height: 100%;
vertical-align: top;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak {
.monaco-workbench .part.statusbar > .statusbar-item.has-beak {
position: relative;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before {
.monaco-workbench .part.statusbar > .statusbar-item.has-beak:before {
content: '';
position: absolute;
left: 11px;
@@ -48,54 +33,66 @@
border-right: 5px solid transparent;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item > :first-child {
.monaco-workbench .part.statusbar > .statusbar-item.left > :first-child,
.monaco-workbench .part.statusbar > .statusbar-item.right > :first-child
{
margin-right: 3px;
margin-left: 3px;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item.left.first-visible-item {
padding-left: 7px; /* Add padding to the most left status bar item */
.monaco-workbench .part.statusbar > .statusbar-item.right {
float: right;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item.right.last-visible-item {
padding-right: 7px; /* Add padding to the most right status bar item */
/* adding padding to the most left status bar item */
.monaco-workbench .part.statusbar > .statusbar-item.left:first-child,
.monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.left {
padding-left: 7px;
}
/* adding padding to the most right status bar item */
.monaco-workbench .part.statusbar > .statusbar-item.right:first-child {
padding-right: 7px;
}
/* tweak appearance for items with background to improve hover feedback */
.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-background-color.left.first-visible-item,
.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-background-color.right.last-visible-item {
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.left:first-child,
.monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.has-background-color.left,
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.right:first-child {
padding-right: 0;
padding-left: 0;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-background-color > :first-child {
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.left > :first-child,
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.right > :first-child
{
margin-right: 0;
margin-left: 0;
padding-left: 10px;
padding-right: 10px;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item a {
.monaco-workbench .part.statusbar > .statusbar-item a {
cursor: pointer;
display: inline-block;
height: 100%;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-entry > span {
.monaco-workbench .part.statusbar > .statusbar-entry > span {
height: 100%;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-entry > span,
.monaco-workbench .part.statusbar > .items-container > .statusbar-entry > a {
.monaco-workbench .part.statusbar > .statusbar-entry > span,
.monaco-workbench .part.statusbar > .statusbar-entry > a {
padding: 0 5px 0 5px;
white-space: pre; /* gives some degree of styling */
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-entry span.octicon {
.monaco-workbench .part.statusbar > .statusbar-entry span.octicon {
text-align: center;
font-size: 14px;
}
.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:hover {
.monaco-workbench .part.statusbar > .statusbar-item a:hover {
text-decoration: none;
}

View File

@@ -14,21 +14,11 @@ export interface IStatusbarItem {
}
export class StatusbarItemDescriptor {
readonly syncDescriptor: SyncDescriptor0<IStatusbarItem>;
readonly id: string;
readonly name: string;
readonly alignment: StatusbarAlignment;
readonly priority: number;
syncDescriptor: SyncDescriptor0<IStatusbarItem>;
alignment: StatusbarAlignment;
priority: number;
constructor(
ctor: IConstructorSignature0<IStatusbarItem>,
id: string,
name: string,
alignment?: StatusbarAlignment,
priority?: number
) {
this.id = id;
this.name = name;
constructor(ctor: IConstructorSignature0<IStatusbarItem>, alignment?: StatusbarAlignment, priority?: number) {
this.syncDescriptor = createSyncDescriptor(ctor);
this.alignment = alignment || StatusbarAlignment.LEFT;
this.priority = priority || 0;
@@ -36,16 +26,21 @@ export class StatusbarItemDescriptor {
}
export interface IStatusbarRegistry {
readonly items: StatusbarItemDescriptor[];
registerStatusbarItem(descriptor: StatusbarItemDescriptor): void;
items: StatusbarItemDescriptor[];
}
class StatusbarRegistry implements IStatusbarRegistry {
private readonly _items: StatusbarItemDescriptor[] = [];
get items(): StatusbarItemDescriptor[] { return this._items; }
private _items: StatusbarItemDescriptor[];
constructor() {
this._items = [];
}
get items(): StatusbarItemDescriptor[] {
return this._items;
}
registerStatusbarItem(descriptor: StatusbarItemDescriptor): void {
this._items.push(descriptor);

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/statusbarpart';
import * as nls from 'vs/nls';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { dispose, IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { Registry } from 'vs/platform/registry/common/platform';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -17,308 +17,28 @@ import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiat
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { StatusbarAlignment, IStatusbarService, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Action, IAction } from 'vs/base/common/actions';
import { Action } from 'vs/base/common/actions';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector, ThemeColor } from 'vs/platform/theme/common/themeService';
import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER } from 'vs/workbench/common/theme';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { isThemeColor } from 'vs/editor/common/editorCommon';
import { Color } from 'vs/base/common/color';
import { addClass, EventHelper, createStyleSheet, addDisposableListener, addClasses, clearNode, removeClass, EventType, hide, show, removeClasses } from 'vs/base/browser/dom';
import { addClass, EventHelper, createStyleSheet, addDisposableListener, addClasses, clearNode, removeClass } from 'vs/base/browser/dom';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { coalesce } from 'vs/base/common/arrays';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { values } from 'vs/base/common/map';
interface IPendingStatusbarEntry {
id: string;
name: string;
entry: IStatusbarEntry;
alignment: StatusbarAlignment;
priority: number;
accessor?: IStatusbarEntryAccessor;
}
interface IStatusbarViewModelEntry {
id: string;
name: string;
alignment: StatusbarAlignment;
priority: number;
container: HTMLElement;
}
class StatusbarViewModel extends Disposable {
private static readonly HIDDEN_ENTRIES_KEY = 'workbench.statusbar.hidden';
private readonly _entries: IStatusbarViewModelEntry[] = [];
get entries(): IStatusbarViewModelEntry[] { return this._entries; }
private hidden: Set<string>;
constructor(private storageService: IStorageService) {
super();
this.restoreState();
this.registerListeners();
}
private restoreState(): void {
const hiddenRaw = this.storageService.get(StatusbarViewModel.HIDDEN_ENTRIES_KEY, StorageScope.GLOBAL);
if (hiddenRaw) {
try {
const hiddenArray: string[] = JSON.parse(hiddenRaw);
this.hidden = new Set(hiddenArray);
} catch (error) {
// ignore parsing errors
}
}
if (!this.hidden) {
this.hidden = new Set<string>();
}
}
private registerListeners(): void {
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
}
private onDidStorageChange(event: IWorkspaceStorageChangeEvent): void {
if (event.key === StatusbarViewModel.HIDDEN_ENTRIES_KEY && event.scope === StorageScope.GLOBAL) {
// Keep current hidden entries
const currentlyHidden = new Set(this.hidden);
// Load latest state of hidden entries
this.hidden.clear();
this.restoreState();
const changed = new Set<string>();
// Check for each entry that is now visible
currentlyHidden.forEach(id => {
if (!this.hidden.has(id)) {
changed.add(id);
}
});
// Check for each entry that is now hidden
this.hidden.forEach(id => {
if (!currentlyHidden.has(id)) {
changed.add(id);
}
});
// Update visibility for entries have changed
if (changed.size > 0) {
this._entries.forEach(entry => {
if (changed.has(entry.id)) {
this.updateVisibility(entry.id);
changed.delete(entry.id);
}
});
}
}
}
add(entry: IStatusbarViewModelEntry): IDisposable {
this._entries.push(entry); // intentionally not using a map here since multiple entries can have the same ID!
// Update visibility directly
this.updateVisibility(entry);
// Sort according to priority
this.sort();
// Mark first/last visible entry
this.markFirstLastVisibleEntry();
return toDisposable(() => this.remove(entry));
}
private remove(entry: IStatusbarViewModelEntry): void {
const index = this._entries.indexOf(entry);
if (index >= 0) {
this._entries.splice(index, 1);
// Mark first/last visible entry
this.markFirstLastVisibleEntry();
}
}
isHidden(id: string): boolean {
return this.hidden.has(id);
}
hide(id: string): void {
if (!this.hidden.has(id)) {
this.hidden.add(id);
this.updateVisibility(id);
this.saveState();
}
}
show(id: string): void {
if (this.hidden.has(id)) {
this.hidden.delete(id);
this.updateVisibility(id);
this.saveState();
}
}
findEntry(container: HTMLElement): IStatusbarViewModelEntry | undefined {
for (const entry of this._entries) {
if (entry.container === container) {
return entry;
}
}
return undefined;
}
getEntries(alignment: StatusbarAlignment): IStatusbarViewModelEntry[] {
return this._entries.filter(entry => entry.alignment === alignment);
}
private updateVisibility(id: string): void;
private updateVisibility(entry: IStatusbarViewModelEntry): void;
private updateVisibility(arg1: string | IStatusbarViewModelEntry): void {
// By identifier
if (typeof arg1 === 'string') {
const id = arg1;
for (const entry of this._entries) {
if (entry.id === id) {
this.updateVisibility(entry);
}
}
}
// By entry
else {
const entry = arg1;
const isHidden = this.isHidden(entry.id);
// Use CSS to show/hide item container
if (isHidden) {
hide(entry.container);
} else {
show(entry.container);
}
// Mark first/last visible entry
this.markFirstLastVisibleEntry();
}
}
private saveState(): void {
if (this.hidden.size > 0) {
this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(values(this.hidden)), StorageScope.GLOBAL);
} else {
this.storageService.remove(StatusbarViewModel.HIDDEN_ENTRIES_KEY, StorageScope.GLOBAL);
}
}
private sort(): void {
this._entries.sort((entryA, entryB) => {
if (entryA.alignment === entryB.alignment) {
return entryB.priority - entryA.priority; // higher priority towards the left
}
if (entryA.alignment === StatusbarAlignment.LEFT) {
return -1;
}
if (entryB.alignment === StatusbarAlignment.LEFT) {
return 1;
}
return 0;
});
}
private markFirstLastVisibleEntry(): void {
this.doMarkFirstLastVisibleStatusbarItem(this.getEntries(StatusbarAlignment.LEFT));
this.doMarkFirstLastVisibleStatusbarItem(this.getEntries(StatusbarAlignment.RIGHT));
}
private doMarkFirstLastVisibleStatusbarItem(entries: IStatusbarViewModelEntry[]): void {
let firstVisibleItem: IStatusbarViewModelEntry | undefined;
let lastVisibleItem: IStatusbarViewModelEntry | undefined;
for (const entry of entries) {
// Clear previous first
removeClasses(entry.container, 'first-visible-item', 'last-visible-item');
const isVisible = !this.isHidden(entry.id);
if (isVisible) {
if (!firstVisibleItem) {
firstVisibleItem = entry;
}
lastVisibleItem = entry;
}
}
// Mark: first visible item
if (firstVisibleItem) {
addClass(firstVisibleItem.container, 'first-visible-item');
}
// Mark: last visible item
if (lastVisibleItem) {
addClass(lastVisibleItem.container, 'last-visible-item');
}
}
}
class ToggleStatusbarEntryVisibilityAction extends Action {
constructor(id: string, label: string, private model: StatusbarViewModel) {
super(id, label, undefined, true);
this.checked = !model.isHidden(id);
}
run(): Promise<any> {
if (this.model.isHidden(this.id)) {
this.model.show(this.id);
} else {
this.model.hide(this.id);
}
return Promise.resolve(true);
}
}
class HideStatusbarEntryAction extends Action {
constructor(id: string, private model: StatusbarViewModel) {
super(id, nls.localize('hide', "Hide"), undefined, true);
}
run(): Promise<any> {
this.model.hide(this.id);
return Promise.resolve(true);
}
}
interface PendingEntry { entry: IStatusbarEntry; alignment: StatusbarAlignment; priority: number; accessor?: IStatusbarEntryAccessor; }
export class StatusbarPart extends Part implements IStatusbarService {
_serviceBrand: ServiceIdentifier<IStatusbarService>;
_serviceBrand: ServiceIdentifier<any>;
private static readonly PRIORITY_PROP = 'statusbar-entry-priority';
private static readonly ALIGNMENT_PROP = 'statusbar-entry-alignment';
//#region IView
@@ -329,27 +49,20 @@ export class StatusbarPart extends Part implements IStatusbarService {
//#endregion
private statusMessageDispose: IDisposable;
private styleElement: HTMLStyleElement;
private pendingEntries: IPendingStatusbarEntry[] = [];
private readonly viewModel: StatusbarViewModel;
private leftItemsContainer: HTMLElement;
private rightItemsContainer: HTMLElement;
private pendingEntries: PendingEntry[] = [];
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IContextMenuService private contextMenuService: IContextMenuService
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
) {
super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
this.viewModel = this._register(new StatusbarViewModel(storageService));
this.registerListeners();
}
@@ -357,233 +70,133 @@ export class StatusbarPart extends Part implements IStatusbarService {
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateStyles()));
}
addEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number = 0): IStatusbarEntryAccessor {
addEntry(entry: IStatusbarEntry, alignment: StatusbarAlignment, priority: number = 0): IStatusbarEntryAccessor {
// As long as we have not been created into a container yet, record all entries
// that are pending so that they can get created at a later point
if (!this.element) {
return this.doAddPendingEntry(entry, id, name, alignment, priority);
const pendingEntry: PendingEntry = {
entry, alignment, priority
};
this.pendingEntries.push(pendingEntry);
const accessor: IStatusbarEntryAccessor = {
update: (entry: IStatusbarEntry) => {
if (pendingEntry.accessor) {
pendingEntry.accessor.update(entry);
} else {
pendingEntry.entry = entry;
}
},
dispose: () => {
if (pendingEntry.accessor) {
pendingEntry.accessor.dispose();
} else {
this.pendingEntries = this.pendingEntries.filter(entry => entry !== pendingEntry);
}
}
};
return accessor;
}
// Otherwise add to view
return this.doAddEntry(entry, id, name, alignment, priority);
}
// Render entry in status bar
const el = this.doCreateStatusItem(alignment, priority, ...coalesce(['statusbar-entry', entry.showBeak ? 'has-beak' : undefined]));
const item = this.instantiationService.createInstance(StatusBarEntryItem, el, entry);
private doAddPendingEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number): IStatusbarEntryAccessor {
const pendingEntry: IPendingStatusbarEntry = { entry, id, name, alignment, priority };
this.pendingEntries.push(pendingEntry);
const accessor: IStatusbarEntryAccessor = {
update: (entry: IStatusbarEntry) => {
if (pendingEntry.accessor) {
pendingEntry.accessor.update(entry);
} else {
pendingEntry.entry = entry;
}
},
dispose: () => {
if (pendingEntry.accessor) {
pendingEntry.accessor.dispose();
} else {
this.pendingEntries = this.pendingEntries.filter(entry => entry !== pendingEntry);
}
// Insert according to priority
const container = this.element;
const neighbours = this.getEntries(alignment);
let inserted = false;
for (const neighbour of neighbours) {
const nPriority = Number(neighbour.getAttribute(StatusbarPart.PRIORITY_PROP));
if (
alignment === StatusbarAlignment.LEFT && nPriority < priority ||
alignment === StatusbarAlignment.RIGHT && nPriority > priority
) {
container.insertBefore(el, neighbour);
inserted = true;
break;
}
};
}
return accessor;
}
private doAddEntry(entry: IStatusbarEntry, id: string, name: string, alignment: StatusbarAlignment, priority: number): IStatusbarEntryAccessor {
// Create item
const itemContainer = this.doCreateStatusItem(id, name, alignment, priority, ...coalesce(['statusbar-entry', entry.showBeak ? 'has-beak' : undefined]));
const item = this.instantiationService.createInstance(StatusbarEntryItem, itemContainer, entry);
// Append to parent
this.appendOneStatusbarEntry(itemContainer, alignment, priority);
// Add to view model
const viewModelEntry: IStatusbarViewModelEntry = { id, name, alignment, priority, container: itemContainer };
const viewModelEntryDispose = this.viewModel.add(viewModelEntry);
if (!inserted) {
container.appendChild(el);
}
return {
update: entry => {
// Update beak
if (entry.showBeak) {
addClass(itemContainer, 'has-beak');
addClass(el, 'has-beak');
} else {
removeClass(itemContainer, 'has-beak');
removeClass(el, 'has-beak');
}
// Update entry
item.update(entry);
},
dispose: () => {
dispose(viewModelEntryDispose);
itemContainer.remove();
el.remove();
dispose(item);
}
};
}
updateEntryVisibility(id: string, visible: boolean): void {
if (visible) {
this.viewModel.show(id);
} else {
this.viewModel.hide(id);
private getEntries(alignment: StatusbarAlignment): HTMLElement[] {
const entries: HTMLElement[] = [];
const container = this.element;
const children = container.children;
for (let i = 0; i < children.length; i++) {
const childElement = <HTMLElement>children.item(i);
if (Number(childElement.getAttribute(StatusbarPart.ALIGNMENT_PROP)) === alignment) {
entries.push(childElement);
}
}
return entries;
}
createContentArea(parent: HTMLElement): HTMLElement {
this.element = parent;
// Left items container
this.leftItemsContainer = document.createElement('div');
addClasses(this.leftItemsContainer, 'left-items', 'items-container');
this.element.appendChild(this.leftItemsContainer);
// Right items container
this.rightItemsContainer = document.createElement('div');
addClasses(this.rightItemsContainer, 'right-items', 'items-container');
this.element.appendChild(this.rightItemsContainer);
// Context menu support
this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, e => this.showContextMenu(e)));
// Initial status bar entries
this.createInitialStatusbarEntries();
return this.element;
}
private createInitialStatusbarEntries(): void {
// Fill in initial items that were contributed from the registry
const registry = Registry.as<IStatusbarRegistry>(Extensions.Statusbar);
// Create initial items that were contributed from the registry
for (const { id, name, alignment, priority, syncDescriptor } of registry.items) {
const descriptors = registry.items.slice().sort((a, b) => {
if (a.alignment === b.alignment) {
if (a.alignment === StatusbarAlignment.LEFT) {
return b.priority - a.priority;
} else {
return a.priority - b.priority;
}
} else if (a.alignment === StatusbarAlignment.LEFT) {
return 1;
} else if (a.alignment === StatusbarAlignment.RIGHT) {
return -1;
} else {
return 0;
}
});
// Create item
const item = this.instantiationService.createInstance(syncDescriptor);
const itemContainer = this.doCreateStatusItem(id, name, alignment, priority);
this._register(item.render(itemContainer));
for (const descriptor of descriptors) {
const item = this.instantiationService.createInstance(descriptor.syncDescriptor);
const el = this.doCreateStatusItem(descriptor.alignment, descriptor.priority);
// Add to view model
const viewModelEntry: IStatusbarViewModelEntry = { id, name, alignment, priority, container: itemContainer };
this.viewModel.add(viewModelEntry);
this._register(item.render(el));
this.element.appendChild(el);
}
// Add items in order according to alignment
this.appendAllStatusbarEntries();
// Fill in pending entries if any
while (this.pendingEntries.length) {
const pending = this.pendingEntries.shift();
if (pending) {
pending.accessor = this.addEntry(pending.entry, pending.id, pending.name, pending.alignment, pending.priority);
}
}
}
private appendAllStatusbarEntries(): void {
// Append in order of priority
[
...this.viewModel.getEntries(StatusbarAlignment.LEFT),
...this.viewModel.getEntries(StatusbarAlignment.RIGHT).reverse() // reversing due to flex: row-reverse
].forEach(entry => {
const target = entry.alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer;
target.appendChild(entry.container);
});
}
private appendOneStatusbarEntry(itemContainer: HTMLElement, alignment: StatusbarAlignment, priority: number): void {
const entries = this.viewModel.getEntries(alignment);
if (alignment === StatusbarAlignment.RIGHT) {
entries.reverse(); // reversing due to flex: row-reverse
}
const target = alignment === StatusbarAlignment.LEFT ? this.leftItemsContainer : this.rightItemsContainer;
// find an entry that has lower priority than the new one
// and then insert the item before that one
let appended = false;
for (const entry of entries) {
if (
alignment === StatusbarAlignment.LEFT && entry.priority < priority ||
alignment === StatusbarAlignment.RIGHT && entry.priority > priority // reversing due to flex: row-reverse
) {
target.insertBefore(itemContainer, entry.container);
appended = true;
break;
}
}
// Fallback to just appending otherwise
if (!appended) {
target.appendChild(itemContainer);
}
}
private showContextMenu(e: MouseEvent): void {
EventHelper.stop(e, true);
const event = new StandardMouseEvent(e);
let actions: IAction[] | undefined = undefined;
this.contextMenuService.showContextMenu({
getAnchor: () => ({ x: event.posx, y: event.posy }),
getActions: () => {
actions = this.getContextMenuActions(event);
return actions;
},
onHide: () => {
if (actions) {
dispose(actions);
}
}
});
}
private getContextMenuActions(event: StandardMouseEvent): IAction[] {
const actions: Action[] = [];
// Figure out if mouse is over an entry
let statusEntryUnderMouse: IStatusbarViewModelEntry | undefined = undefined;
for (let element: HTMLElement | null = event.target; element; element = element.parentElement) {
const entry = this.viewModel.findEntry(element);
const entry = this.pendingEntries.shift();
if (entry) {
statusEntryUnderMouse = entry;
break;
entry.accessor = this.addEntry(entry.entry, entry.alignment, entry.priority);
}
}
if (statusEntryUnderMouse) {
actions.push(new HideStatusbarEntryAction(statusEntryUnderMouse.id, this.viewModel));
actions.push(new Separator());
}
// Show an entry per known status entry
// Note: even though entries have an identifier, there can be multiple entries
// having the same identifier (e.g. from extensions). So we make sure to only
// show a single entry per identifier we handled.
const handledEntries = new Set<string>();
this.viewModel.entries.forEach(entry => {
if (!handledEntries.has(entry.id)) {
actions.push(new ToggleStatusbarEntryVisibilityAction(entry.id, entry.name, this.viewModel));
handledEntries.add(entry.id);
}
});
// Provide an action to hide the status bar at last
actions.push(new Separator());
actions.push(this.instantiationService.createInstance(ToggleStatusbarVisibilityAction, ToggleStatusbarVisibilityAction.ID, nls.localize('hideStatusBar', "Hide Status Bar")));
return actions;
return this.element;
}
updateStyles(): void {
@@ -607,25 +220,64 @@ export class StatusbarPart extends Part implements IStatusbarService {
this.styleElement = createStyleSheet(container);
}
this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .items-container > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`;
this.styleElement.innerHTML = `.monaco-workbench .part.statusbar > .statusbar-item.has-beak:before { border-bottom-color: ${backgroundColor}; }`;
}
private doCreateStatusItem(id: string, name: string, alignment: StatusbarAlignment, priority: number = 0, ...extraClasses: string[]): HTMLElement {
const itemContainer = document.createElement('div');
itemContainer.title = name;
addClass(itemContainer, 'statusbar-item');
private doCreateStatusItem(alignment: StatusbarAlignment, priority: number = 0, ...extraClasses: string[]): HTMLElement {
const el = document.createElement('div');
addClass(el, 'statusbar-item');
if (extraClasses) {
addClasses(itemContainer, ...extraClasses);
addClasses(el, ...extraClasses);
}
if (alignment === StatusbarAlignment.RIGHT) {
addClass(itemContainer, 'right');
addClass(el, 'right');
} else {
addClass(itemContainer, 'left');
addClass(el, 'left');
}
return itemContainer;
el.setAttribute(StatusbarPart.PRIORITY_PROP, String(priority));
el.setAttribute(StatusbarPart.ALIGNMENT_PROP, String(alignment));
return el;
}
setStatusMessage(message: string, autoDisposeAfter: number = -1, delayBy: number = 0): IDisposable {
// Dismiss any previous
dispose(this.statusMessageDispose);
// Create new
let statusMessageEntry: IStatusbarEntryAccessor;
let showHandle: any = setTimeout(() => {
statusMessageEntry = this.addEntry({ text: message }, StatusbarAlignment.LEFT, -Number.MAX_VALUE /* far right on left hand side */);
showHandle = null;
}, delayBy);
let hideHandle: any;
// Dispose function takes care of timeouts and actual entry
const statusMessageDispose = {
dispose: () => {
if (showHandle) {
clearTimeout(showHandle);
}
if (hideHandle) {
clearTimeout(hideHandle);
}
if (statusMessageEntry) {
statusMessageEntry.dispose();
}
}
};
this.statusMessageDispose = statusMessageDispose;
if (typeof autoDisposeAfter === 'number' && autoDisposeAfter > 0) {
hideHandle = setTimeout(() => statusMessageDispose.dispose(), autoDisposeAfter);
}
return statusMessageDispose;
}
layout(width: number, height: number): void {
@@ -639,20 +291,27 @@ export class StatusbarPart extends Part implements IStatusbarService {
}
}
class StatusbarEntryItem extends Disposable {
let manageExtensionAction: ManageExtensionAction;
class StatusBarEntryItem extends Disposable {
private entryDisposables: IDisposable[] = [];
constructor(
private container: HTMLElement,
entry: IStatusbarEntry,
@ICommandService private readonly commandService: ICommandService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@INotificationService private readonly notificationService: INotificationService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IEditorService private readonly editorService: IEditorService,
@IThemeService private readonly themeService: IThemeService
) {
super();
if (!manageExtensionAction) {
manageExtensionAction = this.instantiationService.createInstance(ManageExtensionAction);
}
this.render(entry);
}
@@ -692,6 +351,19 @@ class StatusbarEntryItem extends Disposable {
addClass(this.container, 'has-background-color');
}
// Context Menu
if (entry.extensionId) {
this.entryDisposables.push((addDisposableListener(textContainer, 'contextmenu', e => {
EventHelper.stop(e, true);
this.contextMenuService.showContextMenu({
getAnchor: () => this.container,
getActionsContext: () => entry.extensionId!.value,
getActions: () => [manageExtensionAction]
});
})));
}
this.container.appendChild(textContainer);
}
@@ -740,30 +412,43 @@ class StatusbarEntryItem extends Disposable {
}
}
class ManageExtensionAction extends Action {
constructor(
@ICommandService private readonly commandService: ICommandService
) {
super('statusbar.manage.extension', nls.localize('manageExtension', "Manage Extension"));
}
run(extensionId: string): Promise<any> {
return this.commandService.executeCommand('_extensions.manage', extensionId);
}
}
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const statusBarItemHoverBackground = theme.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND);
if (statusBarItemHoverBackground) {
collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:hover { background-color: ${statusBarItemHoverBackground}; }`);
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a:hover { background-color: ${statusBarItemHoverBackground}; }`);
}
const statusBarItemActiveBackground = theme.getColor(STATUS_BAR_ITEM_ACTIVE_BACKGROUND);
if (statusBarItemActiveBackground) {
collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:active { background-color: ${statusBarItemActiveBackground}; }`);
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a:active { background-color: ${statusBarItemActiveBackground}; }`);
}
const statusBarProminentItemForeground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_FOREGROUND);
if (statusBarProminentItemForeground) {
collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item .status-bar-info { color: ${statusBarProminentItemForeground}; }`);
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item .status-bar-info { color: ${statusBarProminentItemForeground}; }`);
}
const statusBarProminentItemBackground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_BACKGROUND);
if (statusBarProminentItemBackground) {
collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item .status-bar-info { background-color: ${statusBarProminentItemBackground}; }`);
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item .status-bar-info { background-color: ${statusBarProminentItemBackground}; }`);
}
const statusBarProminentItemHoverBackground = theme.getColor(STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND);
if (statusBarProminentItemHoverBackground) {
collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a.status-bar-info:hover { background-color: ${statusBarProminentItemHoverBackground}; }`);
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a.status-bar-info:hover { background-color: ${statusBarProminentItemHoverBackground}; }`);
}
});

View File

@@ -41,7 +41,7 @@
}
/* Windows/Linux: Rules for custom title (icon, window controls) */
.monaco-workbench.web .part.titlebar,
.monaco-workbench.windows .part.titlebar,
.monaco-workbench.linux .part.titlebar {
padding: 0;
@@ -51,7 +51,6 @@
overflow: visible;
}
.monaco-workbench.web .part.titlebar > .window-title,
.monaco-workbench.windows .part.titlebar > .window-title,
.monaco-workbench.linux .part.titlebar > .window-title {
cursor: default;

View File

@@ -13,7 +13,7 @@ import { IAction, Action } from 'vs/base/common/actions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import * as DOM from 'vs/base/browser/dom';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { isMacintosh, isWeb } from 'vs/base/common/platform';
import { isMacintosh, isLinux } from 'vs/base/common/platform';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -34,19 +34,22 @@ import { assign } from 'vs/base/common/objects';
import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
export class MenubarControl extends Disposable {
private keys = [
'files.autoSave',
'window.menuBarVisibility',
'editor.multiCursorModifier',
'workbench.sideBar.location',
'workbench.statusBar.visible',
'workbench.activityBar.visible',
'window.enableMenuBarMnemonics',
'window.nativeTabs'
];
// {{SQL CARBON EDIT}} - Disable unusued menus
private menus: {
private topLevelMenus: {
'File': IMenu;
'Edit': IMenu;
// 'Selection': IMenu;
@@ -76,17 +79,15 @@ export class MenubarControl extends Disposable {
private container: HTMLElement;
private recentlyOpened: IRecentlyOpened;
private alwaysOnMnemonics: boolean;
private isNative: boolean;
private readonly _onVisibilityChange: Emitter<boolean>;
private readonly _onFocusStateChange: Emitter<boolean>;
private menubarService: IMenubarService;
private static MAX_MENU_RECENT_ENTRIES = 10;
constructor(
@IThemeService private readonly themeService: IThemeService,
@IMenubarService private readonly menubarService: IMenubarService,
@IMenuService private readonly menuService: IMenuService,
@IWindowService private readonly windowService: IWindowService,
@IWindowsService private readonly windowsService: IWindowsService,
@@ -99,22 +100,13 @@ export class MenubarControl extends Disposable {
@INotificationService private readonly notificationService: INotificationService,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IInstantiationService private readonly instantiationService: IInstantiationService
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
) {
super();
this.isNative = !isWeb && (isMacintosh || this.currentTitlebarStyleSetting !== 'custom');
this.instantiationService.invokeFunction((accessor: ServicesAccessor) => {
if (this.isNative) {
this.menubarService = accessor.get(IMenubarService);
}
});
// {{SQL CARBON EDIT}} - Disable unusued menus
this.menus = {
// {{SQL CARBON EDIT}} - Disable unused menus
this.topLevelMenus = {
'File': this._register(this.menuService.createMenu(MenuId.MenubarFileMenu, this.contextKeyService)),
'Edit': this._register(this.menuService.createMenu(MenuId.MenubarEditMenu, this.contextKeyService)),
// 'Selection': this._register(this.menuService.createMenu(MenuId.MenubarSelectionMenu, this.contextKeyService)),
@@ -125,9 +117,8 @@ export class MenubarControl extends Disposable {
'Help': this._register(this.menuService.createMenu(MenuId.MenubarHelpMenu, this.contextKeyService))
};
if (isMacintosh && this.isNative) {
this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService));
this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences");
if (isMacintosh) {
this.topLevelMenus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService));
}
this.menuUpdater = this._register(new RunOnceScheduler(() => this.doUpdateMenubar(false), 200));
@@ -135,9 +126,9 @@ export class MenubarControl extends Disposable {
this._onVisibilityChange = this._register(new Emitter<boolean>());
this._onFocusStateChange = this._register(new Emitter<boolean>());
if (this.isNative) {
for (const topLevelMenuName of Object.keys(this.topLevelTitles)) {
const menu = this.menus[topLevelMenuName];
if (isMacintosh || this.currentTitlebarStyleSetting !== 'custom') {
for (const topLevelMenuName of Object.keys(this.topLevelMenus)) {
const menu = this.topLevelMenus[topLevelMenuName];
if (menu) {
this._register(menu.onDidChange(() => this.updateMenubar()));
}
@@ -147,11 +138,13 @@ export class MenubarControl extends Disposable {
this.windowService.getRecentlyOpened().then((recentlyOpened) => {
this.recentlyOpened = recentlyOpened;
if (this.isNative) {
if (isMacintosh || this.currentTitlebarStyleSetting !== 'custom') {
this.doUpdateMenubar(true);
}
});
this.notifyExistingLinuxUser();
this.notifyUserOfCustomMenubarAccessibility();
this.registerListeners();
@@ -166,6 +159,28 @@ export class MenubarControl extends Disposable {
return enableMenuBarMnemonics;
}
private get currentSidebarPosition(): string {
return this.configurationService.getValue<string>('workbench.sideBar.location');
}
private get currentStatusBarVisibility(): boolean {
let setting = this.configurationService.getValue<boolean>('workbench.statusBar.visible');
if (typeof setting !== 'boolean') {
setting = true;
}
return setting;
}
private get currentActivityBarVisibility(): boolean {
let setting = this.configurationService.getValue<boolean>('workbench.activityBar.visible');
if (typeof setting !== 'boolean') {
setting = true;
}
return setting;
}
private get currentMenubarVisibility(): MenuBarVisibility {
return this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility');
}
@@ -202,8 +217,38 @@ export class MenubarControl extends Disposable {
});
}
// TODO@sbatten remove after feb19
private notifyExistingLinuxUser(): void {
if (!isLinux) {
return;
}
const isNewUser = !this.storageService.get('telemetry.lastSessionDate', StorageScope.GLOBAL);
const hasBeenNotified = this.storageService.getBoolean('menubar/linuxTitlebarRevertNotified', StorageScope.GLOBAL, false);
const titleBarConfiguration = this.configurationService.inspect('window.titleBarStyle');
const customShown = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom';
if (!hasBeenNotified) {
this.storageService.store('menubar/linuxTitlebarRevertNotified', true, StorageScope.GLOBAL);
}
if (isNewUser || hasBeenNotified || (titleBarConfiguration && titleBarConfiguration.user) || customShown) {
return;
}
const message = nls.localize('menubar.linuxTitlebarRevertNotification', "We have updated the default title bar on Linux to use the native setting. If you prefer, you can go back to the custom setting. More information is available in our [online documentation](https://go.microsoft.com/fwlink/?linkid=2074137).");
this.notificationService.prompt(Severity.Info, message, [
{
label: nls.localize('goToSetting', "Open Settings"),
run: () => {
return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
}
}
]);
}
private notifyUserOfCustomMenubarAccessibility(): void {
if (isWeb || isMacintosh) {
if (isMacintosh) {
return;
}
@@ -243,7 +288,7 @@ export class MenubarControl extends Disposable {
this._register(this.keybindingService.onDidUpdateKeybindings(() => this.updateMenubar()));
// These listeners only apply when the custom menubar is being used
if (!this.isNative) {
if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') {
// Listen for window focus changes
this._register(this.windowService.onDidChangeFocus(e => this.onDidChangeWindowFocus(e)));
@@ -259,7 +304,7 @@ export class MenubarControl extends Disposable {
}
private doUpdateMenubar(firstTime: boolean): void {
if (!this.isNative) {
if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') {
this.setupCustomMenubar(firstTime);
} else {
// Send menus to main process to be rendered by Electron
@@ -277,6 +322,30 @@ export class MenubarControl extends Disposable {
private calculateActionLabel(action: IAction | IMenubarMenuItemAction): string {
let label = action.label;
switch (action.id) {
case 'workbench.action.toggleSidebarPosition':
if (this.currentSidebarPosition !== 'right') {
label = nls.localize({ key: 'miMoveSidebarRight', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Right");
} else {
label = nls.localize({ key: 'miMoveSidebarLeft', comment: ['&& denotes a mnemonic'] }, "&&Move Side Bar Left");
}
break;
case 'workbench.action.toggleStatusbarVisibility':
if (this.currentStatusBarVisibility) {
label = nls.localize({ key: 'miHideStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Hide Status Bar");
} else {
label = nls.localize({ key: 'miShowStatusbar', comment: ['&& denotes a mnemonic'] }, "&&Show Status Bar");
}
break;
case 'workbench.action.toggleActivityBarVisibility':
if (this.currentActivityBarVisibility) {
label = nls.localize({ key: 'miHideActivityBar', comment: ['&& denotes a mnemonic'] }, "Hide &&Activity Bar");
} else {
label = nls.localize({ key: 'miShowActivityBar', comment: ['&& denotes a mnemonic'] }, "Show &&Activity Bar");
}
break;
default:
break;
}
@@ -403,7 +472,7 @@ export class MenubarControl extends Disposable {
break;
case 'workbench.action.showAboutDialog':
if (!isMacintosh && !isWeb) {
if (!isMacintosh) {
const updateAction = this.getUpdateAction();
if (updateAction) {
updateAction.label = mnemonicMenuLabel(updateAction.label);
@@ -443,7 +512,7 @@ export class MenubarControl extends Disposable {
}
// Update the menu actions
const updateActions = (menu: IMenu, target: IAction[], topLevelTitle: string) => {
const updateActions = (menu: IMenu, target: IAction[]) => {
target.splice(0);
let groups = menu.getActions();
for (let group of groups) {
@@ -452,20 +521,11 @@ export class MenubarControl extends Disposable {
for (let action of actions) {
this.insertActionsBefore(action, target);
if (action instanceof SubmenuItemAction) {
if (!this.menus[action.item.submenu]) {
this.menus[action.item.submenu] = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
const submenu = this.menus[action.item.submenu];
this._register(submenu!.onDidChange(() => {
const actions: IAction[] = [];
updateActions(menu, actions, topLevelTitle);
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[topLevelTitle]) });
}, this));
}
const submenu = this.menus[action.item.submenu]!;
const submenu = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
const submenuActions: SubmenuAction[] = [];
updateActions(submenu, submenuActions, topLevelTitle);
updateActions(submenu, submenuActions);
target.push(new SubmenuAction(mnemonicMenuLabel(action.label), submenuActions));
submenu.dispose();
} else {
action.label = mnemonicMenuLabel(this.calculateActionLabel(action));
target.push(action);
@@ -478,19 +538,19 @@ export class MenubarControl extends Disposable {
target.pop();
};
for (const title of Object.keys(this.topLevelTitles)) {
const menu = this.menus[title];
for (const title of Object.keys(this.topLevelMenus)) {
const menu = this.topLevelMenus[title];
if (firstTime && menu) {
this._register(menu.onDidChange(() => {
const actions: IAction[] = [];
updateActions(menu, actions, title);
updateActions(menu, actions);
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
}));
}
const actions: IAction[] = [];
if (menu) {
updateActions(menu, actions, title);
updateActions(menu, actions);
}
if (!firstTime) {
@@ -531,12 +591,6 @@ export class MenubarControl extends Disposable {
if (menuItem instanceof SubmenuItemAction) {
const submenu = { items: [] };
if (!this.menus[menuItem.item.submenu]) {
this.menus[menuItem.item.submenu] = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService);
this._register(this.menus[menuItem.item.submenu]!.onDidChange(() => this.updateMenubar()));
}
const menuToDispose = this.menuService.createMenu(menuItem.item.submenu, this.contextKeyService);
this.populateMenuItems(menuToDispose, submenu, keybindings);
@@ -583,7 +637,7 @@ export class MenubarControl extends Disposable {
private getAdditionalKeybindings(): { [id: string]: IMenubarKeybinding } {
const keybindings = {};
if (isMacintosh && this.isNative) {
if (isMacintosh) {
keybindings['workbench.action.quit'] = (this.getMenubarKeybinding('workbench.action.quit'));
}
@@ -596,8 +650,8 @@ export class MenubarControl extends Disposable {
}
menubarData.keybindings = this.getAdditionalKeybindings();
for (const topLevelMenuName of Object.keys(this.topLevelTitles)) {
const menu = this.menus[topLevelMenuName];
for (const topLevelMenuName of Object.keys(this.topLevelMenus)) {
const menu = this.topLevelMenus[topLevelMenuName];
if (menu) {
const menubarMenu: IMenubarMenu = { items: [] };
this.populateMenuItems(menu, menubarMenu, menubarData.keybindings);
@@ -644,7 +698,7 @@ export class MenubarControl extends Disposable {
// Build the menubar
if (this.container) {
if (!this.isNative) {
if (!isMacintosh && this.currentTitlebarStyleSetting === 'custom') {
this.doUpdateMenubar(true);
}
}

View File

@@ -22,7 +22,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER } from 'vs/workbench/common/theme';
import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform';
import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { Color } from 'vs/base/common/color';
import { trim } from 'vs/base/common/strings';
@@ -50,8 +50,8 @@ export class TitlebarPart extends Part implements ITitleService {
readonly minimumWidth: number = 0;
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
get minimumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
get maximumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
get minimumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
get maximumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
//#endregion
@@ -135,9 +135,9 @@ export class TitlebarPart extends Part implements ITitleService {
}
private onMenubarVisibilityChanged(visible: boolean) {
if (isWeb || isWindows || isLinux) {
if (isWindows || isLinux) {
// Hide title when toggling menu bar
if (!isWeb && this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'toggle' && visible) {
if (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'toggle' && visible) {
// Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor
hide(this.dragRegion);
setTimeout(() => show(this.dragRegion), 50);
@@ -150,7 +150,7 @@ export class TitlebarPart extends Part implements ITitleService {
}
private onMenubarFocusChanged(focused: boolean) {
if (!isWeb && (isWindows || isLinux)) {
if (isWindows || isLinux) {
if (focused) {
hide(this.dragRegion);
} else {
@@ -207,7 +207,7 @@ export class TitlebarPart extends Part implements ITitleService {
this.pendingTitle = title;
}
if ((isWeb || isWindows || isLinux) && this.title) {
if ((isWindows || isLinux) && this.title) {
if (this.lastLayoutDimensions) {
this.updateLayout(this.lastLayoutDimensions);
}
@@ -322,12 +322,10 @@ export class TitlebarPart extends Part implements ITitleService {
this.element = parent;
// Draggable region that we can manipulate for #52522
if (!isWeb) {
this.dragRegion = append(this.element, $('div.titlebar-drag-region'));
}
this.dragRegion = append(this.element, $('div.titlebar-drag-region'));
// App Icon (Native Windows/Linux)
if (!isMacintosh && !isWeb) {
// App Icon (Windows/Linux)
if (!isMacintosh) {
this.appIcon = append(this.element, $('div.window-appicon'));
this.onUpdateAppIconDragBehavior();
@@ -343,7 +341,7 @@ export class TitlebarPart extends Part implements ITitleService {
this.menubarPart.create(this.menubar);
if (!isMacintosh || isWeb) {
if (!isMacintosh) {
this._register(this.menubarPart.onVisibilityChange(e => this.onMenubarVisibilityChanged(e)));
this._register(this.menubarPart.onFocusStateChange(e => this.onMenubarFocusChanged(e)));
}
@@ -357,7 +355,7 @@ export class TitlebarPart extends Part implements ITitleService {
}
// Maximize/Restore on doubleclick
if (isMacintosh && !isWeb) {
if (isMacintosh) {
this._register(addDisposableListener(this.element, EventType.DBLCLICK, e => {
EventHelper.stop(e);
@@ -376,8 +374,8 @@ export class TitlebarPart extends Part implements ITitleService {
}));
});
// Window Controls (Native Windows/Linux)
if (!isMacintosh && !isWeb) {
// Window Controls (Windows/Linux)
if (!isMacintosh) {
this.windowControls = append(this.element, $('div.window-controls-container'));
@@ -548,24 +546,17 @@ export class TitlebarPart extends Part implements ITitleService {
}
private adjustTitleMarginToCenter(): void {
if (!isMacintosh || isWeb) {
const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10;
const rightMarker = this.element.clientWidth - (this.windowControls ? this.windowControls.clientWidth : 0) - 10;
// Not enough space to center the titlebar within window,
// Center between menu and window controls
if (leftMarker > (this.element.clientWidth - this.title.clientWidth) / 2 ||
rightMarker < (this.element.clientWidth + this.title.clientWidth) / 2) {
this.title.style.position = null;
this.title.style.left = null;
this.title.style.transform = null;
return;
}
if (!isMacintosh &&
(this.appIcon.clientWidth + this.menubar.clientWidth + 10 > (this.element.clientWidth - this.title.clientWidth) / 2 ||
this.element.clientWidth - this.windowControls.clientWidth - 10 < (this.element.clientWidth + this.title.clientWidth) / 2)) {
this.title.style.position = null;
this.title.style.left = null;
this.title.style.transform = null;
} else {
this.title.style.position = 'absolute';
this.title.style.left = '50%';
this.title.style.transform = 'translate(-50%, 0)';
}
this.title.style.position = 'absolute';
this.title.style.left = '50%';
this.title.style.transform = 'translate(-50%, 0)';
}
updateLayout(dimension: Dimension): void {
@@ -573,15 +564,15 @@ export class TitlebarPart extends Part implements ITitleService {
if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
// Only prevent zooming behavior on macOS or when the menubar is not visible
if ((!isWeb && isMacintosh) || this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden') {
if (isMacintosh || this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden') {
this.title.style.zoom = `${1 / getZoomFactor()}`;
if (!isWeb && (isWindows || isLinux)) {
if (isWindows || isLinux) {
this.appIcon.style.zoom = `${1 / getZoomFactor()}`;
this.windowControls.style.zoom = `${1 / getZoomFactor()}`;
}
} else {
this.title.style.zoom = null;
if (!isWeb && (isWindows || isLinux)) {
if (isWindows || isLinux) {
this.appIcon.style.zoom = null;
this.windowControls.style.zoom = null;
}

View File

@@ -17,7 +17,7 @@ import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeVie
import { IViewletViewOptions, FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { IProgressService2 } from 'vs/platform/progress/common/progress';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -60,9 +60,9 @@ export class CustomTreeViewPanel extends ViewletPanel {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
const { treeView } = (<ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(options.id));
this.treeView = treeView;
this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this));
this._register(toDisposable(() => this.treeView.setVisibility(false)));
this._register(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility()));
this.treeView.onDidChangeActions(() => this.updateActions(), this, this.disposables);
this.disposables.push(toDisposable(() => this.treeView.setVisibility(false)));
this.disposables.push(this.onDidChangeBodyVisibility(() => this.updateTreeVisibility()));
this.updateTreeVisibility();
}
@@ -98,6 +98,11 @@ export class CustomTreeViewPanel extends ViewletPanel {
private updateTreeVisibility(): void {
this.treeView.setVisibility(this.isBodyVisible());
}
dispose(): void {
dispose(this.disposables);
super.dispose();
}
}
class TitleMenus implements IDisposable {
@@ -211,7 +216,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ICommandService private readonly commandService: ICommandService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IProgressService private readonly progressService: IProgressService
@IProgressService2 private readonly progressService: IProgressService2
) {
super();
this.root = new Root();

View File

@@ -46,16 +46,16 @@ export abstract class ViewletPanel extends Panel implements IView {
private static AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
private _onDidFocus = this._register(new Emitter<void>());
private _onDidFocus = new Emitter<void>();
readonly onDidFocus: Event<void> = this._onDidFocus.event;
private _onDidBlur = this._register(new Emitter<void>());
private _onDidBlur = new Emitter<void>();
readonly onDidBlur: Event<void> = this._onDidBlur.event;
private _onDidChangeBodyVisibility = this._register(new Emitter<boolean>());
private _onDidChangeBodyVisibility = new Emitter<boolean>();
readonly onDidChangeBodyVisibility: Event<boolean> = this._onDidChangeBodyVisibility.event;
protected _onDidChangeTitleArea = this._register(new Emitter<void>());
protected _onDidChangeTitleArea = new Emitter<void>();
readonly onDidChangeTitleArea: Event<void> = this._onDidChangeTitleArea.event;
private _isVisible: boolean = false;
@@ -78,6 +78,8 @@ export abstract class ViewletPanel extends Panel implements IView {
this.id = options.id;
this.title = options.title;
this.actionRunner = options.actionRunner;
this.disposables.push(this._onDidFocus, this._onDidBlur, this._onDidChangeBodyVisibility, this._onDidChangeTitleArea);
}
setVisible(visible: boolean): void {
@@ -111,9 +113,9 @@ export abstract class ViewletPanel extends Panel implements IView {
super.render();
const focusTracker = trackFocus(this.element);
this._register(focusTracker);
this._register(focusTracker.onDidFocus(() => this._onDidFocus.fire()));
this._register(focusTracker.onDidBlur(() => this._onDidBlur.fire()));
this.disposables.push(focusTracker);
this.disposables.push(focusTracker.onDidFocus(() => this._onDidFocus.fire()));
this.disposables.push(focusTracker.onDidBlur(() => this._onDidBlur.fire()));
}
protected renderHeader(container: HTMLElement): void {
@@ -130,11 +132,11 @@ export abstract class ViewletPanel extends Panel implements IView {
actionRunner: this.actionRunner
});
this._register(this.toolbar);
this.disposables.push(this.toolbar);
this.setActions();
const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewletPanel.AlwaysShowActionsConfig));
this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this));
onDidRelevantConfigurationChange(this.updateActionsVisibility, this, this.disposables);
this.updateActionsVisibility();
}
@@ -353,7 +355,7 @@ export class PanelViewlet extends Viewlet {
headerBorder: SIDE_BAR_SECTION_HEADER_BORDER,
dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND
}, panel);
const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, panelStyler, onDidChange);
const disposable = combinedDisposable([onDidFocus, onDidChangeTitleArea, panelStyler, onDidChange]);
const panelItem: IViewletPanelItem = { panel, disposable };
this.panelItems.splice(index, 0, panelItem);

View File

@@ -29,15 +29,14 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
import { localize } from 'vs/nls';
import { IAddedViewDescriptorRef, IViewDescriptorRef, PersistentContributableViewsModel } from 'vs/workbench/browser/parts/views/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { MementoObject } from 'vs/workbench/common/memento';
export interface IViewletViewOptions extends IViewletPanelOptions {
viewletState: MementoObject;
viewletState: object;
}
export abstract class ViewContainerViewlet extends PanelViewlet implements IViewsViewlet {
private readonly viewletState: MementoObject;
private readonly viewletState: object;
private didLayout = false;
private dimension: DOM.Dimension;
private areExtensionsReady: boolean = false;
@@ -208,7 +207,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
this.viewsModel.setCollapsed(viewDescriptor.id, collapsed);
});
this.viewDisposables.splice(index, 0, combinedDisposable(contextMenuDisposable, collapseDisposable));
this.viewDisposables.splice(index, 0, combinedDisposable([contextMenuDisposable, collapseDisposable]));
panelsToAdd.push({ panel, size: size || panel.minimumSize, index });
}