Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closes #92135 - Adds view-level progress indicator #92136

Merged
merged 5 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vs/workbench/browser/parts/compositePart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Instantiate composite from registry otherwise
const compositeDescriptor = this.registry.getComposite(id);
if (compositeDescriptor) {
const compositeProgressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, assertIsDefined(this.progressBar), compositeDescriptor.id, !!isActive);
const compositeProgressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, assertIsDefined(this.progressBar), compositeDescriptor.id, !!isActive, undefined);
const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection(
[IEditorProgressService, compositeProgressIndicator] // provide the editor progress service for any editors instantiated within the composite
));
Expand Down
12 changes: 12 additions & 0 deletions src/vs/workbench/browser/parts/views/media/paneviewlet.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
border-top: none !important; /* less clutter: do not show any border for first views in a pane */
}

.monaco-pane-view .pane > .pane-header {
position: relative;
}

.monaco-pane-view .pane > .pane-header > .actions.show {
display: initial;
}
Expand All @@ -23,3 +27,11 @@
.monaco-pane-view .pane > .pane-header h3.title:first-child {
margin-left: 7px;
}

.monaco-pane-view .pane > .pane-header .monaco-progress-container {
position: absolute;
left: 0;
bottom: 0;
z-index: 5;
height: 2px;
}
32 changes: 31 additions & 1 deletion src/vs/workbench/browser/parts/views/viewPaneContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'vs/css!./media/paneviewlet';
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry';
import { attachStyler, IColorMapping, attachButtonStyler, attachLinkStyler } from 'vs/platform/theme/common/styler';
import { attachStyler, IColorMapping, attachButtonStyler, attachLinkStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER, PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener, removeClass, addClass } from 'vs/base/browser/dom';
import { IDisposable, combinedDisposable, dispose, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
Expand Down Expand Up @@ -44,6 +44,9 @@ import { Button } from 'vs/base/browser/ui/button/button';
import { Link } from 'vs/platform/opener/browser/link';
import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator';
import { IProgressIndicator } from 'vs/platform/progress/common/progress';

export interface IPaneColors extends IColorMapping {
dropBackground?: ColorIdentifier;
Expand Down Expand Up @@ -181,6 +184,8 @@ export abstract class ViewPane extends Pane implements IView {
title: string;

private readonly menuActions: ViewMenuActions;
private progressBar!: ProgressBar;
private progressIndicator!: IProgressIndicator;

private toolbar?: ToolBar;
private readonly showActionsAlways: boolean = false;
Expand Down Expand Up @@ -289,6 +294,13 @@ export abstract class ViewPane extends Pane implements IView {
const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewPane.AlwaysShowActionsConfig));
this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this));
this.updateActionsVisibility();

if (this.progressBar !== undefined) {
// Progress bar
this.progressBar = this._register(new ProgressBar(this.headerContainer));
this._register(attachProgressBarStyler(this.progressBar, this.themeService));
this.progressBar.hide();
}
}

protected renderTwisties(container: HTMLElement): void {
Expand Down Expand Up @@ -320,6 +332,24 @@ export abstract class ViewPane extends Pane implements IView {
// noop
}

getProgressIndicator() {
if (!this.headerContainer) {
return undefined;
}

if (this.progressBar === undefined) {
// Progress bar
this.progressBar = this._register(new ProgressBar(this.headerContainer));
this._register(attachProgressBarStyler(this.progressBar, this.themeService));
this.progressBar.hide();
}

if (this.progressIndicator === undefined) {
this.progressIndicator = this.instantiationService.createInstance(CompositeProgressIndicator, assertIsDefined(this.progressBar), this.id, this.isVisible(), { exclusiveProgressBar: true });
}
return this.progressIndicator;
}

protected getProgressLocation(): string {
return this.viewDescriptorService.getViewContainer(this.id)!.id;
}
Expand Down
18 changes: 18 additions & 0 deletions src/vs/workbench/browser/parts/views/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { Viewlet, ViewletDescriptor, ViewletRegistry, Extensions as ViewletExten
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { URI } from 'vs/base/common/uri';
import { IProgressIndicator } from 'vs/platform/progress/common/progress';

export interface IViewState {
visibleGlobal: boolean | undefined;
Expand Down Expand Up @@ -465,6 +466,8 @@ export class ViewsService extends Disposable implements IViewsService {

private readonly visibleViewContextKeys: Map<string, IContextKey<boolean>>;

private readonly viewPaneContainers: Map<string, ViewPaneContainer>;

constructor(
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
@IPanelService private readonly panelService: IPanelService,
Expand All @@ -476,6 +479,7 @@ export class ViewsService extends Disposable implements IViewsService {
this.viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
this.viewDisposable = new Map<IViewDescriptor, IDisposable>();
this.visibleViewContextKeys = new Map<string, IContextKey<boolean>>();
this.viewPaneContainers = new Map<string, ViewPaneContainer>();

this._register(toDisposable(() => {
this.viewDisposable.forEach(disposable => disposable.dispose());
Expand All @@ -484,12 +488,16 @@ export class ViewsService extends Disposable implements IViewsService {

this.viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer, this.viewContainersRegistry.getViewContainerLocation(viewContainer)));
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => this.onDidRegisterViewContainer(viewContainer, viewContainerLocation)));

this._register(this.viewContainersRegistry.onDidDeregister(e => this.viewPaneContainers.delete(e.viewContainer.id)));
}

private registerViewPaneContainer(viewPaneContainer: ViewPaneContainer): void {
this._register(viewPaneContainer.onDidAddViews(views => this.onViewsAdded(views)));
this._register(viewPaneContainer.onDidChangeViewVisibility(view => this.onViewsVisibilityChanged(view, view.isBodyVisible())));
this._register(viewPaneContainer.onDidRemoveViews(views => this.onViewsRemoved(views)));

this.viewPaneContainers.set(viewPaneContainer.getId(), viewPaneContainer);
}

private onViewsAdded(added: IView[]): void {
Expand Down Expand Up @@ -699,6 +707,16 @@ export class ViewsService extends Disposable implements IViewsService {
return null;
}

getProgressIndicator(id: string): IProgressIndicator | undefined {
const viewContainer = this.viewDescriptorService.getViewContainer(id);
if (viewContainer === null) {
return undefined;
}

const view = this.viewPaneContainers.get(viewContainer.id)?.getView(id);
return view?.getProgressIndicator();
}

private registerViewletOrPanel(viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation): void {
switch (viewContainerLocation) {
case ViewContainerLocation.Panel:
Expand Down
3 changes: 3 additions & 0 deletions src/vs/workbench/common/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { flatten, mergeSort } from 'vs/base/common/arrays';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { SetMap } from 'vs/base/common/collections';
import { IProgressIndicator } from 'vs/platform/progress/common/progress';

export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test';

Expand Down Expand Up @@ -402,6 +403,7 @@ export interface IView {

setExpanded(expanded: boolean): boolean;

getProgressIndicator(): IProgressIndicator | undefined;
}

export interface IViewsViewlet extends IViewlet {
Expand All @@ -426,6 +428,7 @@ export interface IViewsService {

closeView(id: string): void;

getProgressIndicator(id: string): IProgressIndicator | undefined;
}

/**
Expand Down
18 changes: 14 additions & 4 deletions src/vs/workbench/services/progress/browser/progressIndicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IProgressRunner, IProgressIndicator, emptyProgressRunner } from 'vs/platform/progress/common/progress';
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { IViewsService } from 'vs/workbench/common/views';

export class ProgressBarIndicator extends Disposable implements IProgressIndicator {

Expand Down Expand Up @@ -153,6 +154,7 @@ export abstract class CompositeScope extends Disposable {
constructor(
private viewletService: IViewletService,
private panelService: IPanelService,
private viewsService: IViewsService,
private scopeId: string
) {
super();
Expand All @@ -161,6 +163,8 @@ export abstract class CompositeScope extends Disposable {
}

registerListeners(): void {
this._register(this.viewsService.onDidChangeViewVisibility(e => e.visible ? this.onScopeOpened(e.id) : this.onScopeClosed(e.id)));

this._register(this.viewletService.onDidViewletOpen(viewlet => this.onScopeOpened(viewlet.getId())));
this._register(this.panelService.onDidPanelOpen(({ panel }) => this.onScopeOpened(panel.getId())));

Expand Down Expand Up @@ -194,17 +198,23 @@ export class CompositeProgressIndicator extends CompositeScope implements IProgr
progressbar: ProgressBar,
scopeId: string,
isActive: boolean,
private readonly options: { exclusiveProgressBar?: boolean } | undefined,
@IViewletService viewletService: IViewletService,
@IPanelService panelService: IPanelService
@IPanelService panelService: IPanelService,
@IViewsService viewsService: IViewsService
) {
super(viewletService, panelService, scopeId);
super(viewletService, panelService, viewsService, scopeId);

this.progressbar = progressbar;
this.isActive = isActive || isUndefinedOrNull(scopeId); // If service is unscoped, enable by default
}

onScopeDeactivated(): void {
this.isActive = false;

if (this.options?.exclusiveProgressBar) {
this.progressbar.stop().hide();
}
}

onScopeActivated(): void {
Expand Down Expand Up @@ -304,7 +314,7 @@ export class CompositeProgressIndicator extends CompositeScope implements IProgr
done: () => {
this.progressState = ProgressIndicatorState.Done;

if (this.isActive) {
if (this.isActive || this.options?.exclusiveProgressBar) {
this.progressbar.stop().hide();
}
}
Expand Down Expand Up @@ -335,7 +345,7 @@ export class CompositeProgressIndicator extends CompositeScope implements IProgr
// The while promise is either null or equal the promise we last hooked on
this.progressState = ProgressIndicatorState.None;

if (this.isActive) {
if (this.isActive || this.options?.exclusiveProgressBar) {
this.progressbar.stop().hide();
}
}
Expand Down
48 changes: 36 additions & 12 deletions src/vs/workbench/services/progress/browser/progressService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventHelper } from 'vs/base/browser/dom';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { parseLinkedText } from 'vs/base/common/linkedText';
import { IViewsService, IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views';

export class ProgressService extends Disposable implements IProgressService {

Expand All @@ -33,6 +34,8 @@ export class ProgressService extends Disposable implements IProgressService {
constructor(
@IActivityService private readonly activityService: IActivityService,
@IViewletService private readonly viewletService: IViewletService,
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
@IViewsService private readonly viewsService: IViewsService,
@IPanelService private readonly panelService: IPanelService,
@INotificationService private readonly notificationService: INotificationService,
@IStatusbarService private readonly statusbarService: IStatusbarService,
Expand All @@ -54,6 +57,10 @@ export class ProgressService extends Disposable implements IProgressService {
return this.withPanelProgress(location, task, { ...options, location });
}

if (this.viewsService.getProgressIndicator(location)) {
return this.withViewProgress(location, task, { ...options, location });
}

throw new Error(`Bad progress location: ${location}`);
}

Expand Down Expand Up @@ -376,18 +383,38 @@ export class ProgressService extends Disposable implements IProgressService {
// show in viewlet
const promise = this.withCompositeProgress(this.viewletService.getProgressIndicator(viewletId), task, options);

// show activity bar
// show on activity bar
this.showOnActivityBar<P, R>(viewletId, options, promise);

return promise;
}

private withViewProgress<P extends Promise<R>, R = unknown>(viewId: string, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P {

// show in viewlet
const promise = this.withCompositeProgress(this.viewsService.getProgressIndicator(viewId), task, options);

const location = this.viewDescriptorService.getViewLocation(viewId);
if (location !== ViewContainerLocation.Sidebar) {
return promise;
}

const viewletId = this.viewDescriptorService.getViewContainer(viewId)?.id;
eamodio marked this conversation as resolved.
Show resolved Hide resolved
if (viewletId === undefined) {
return promise;
}

// show on activity bar
this.showOnActivityBar(viewletId, options, promise);

return promise;
}

private showOnActivityBar<P extends Promise<R>, R = unknown>(viewletId: string, options: IProgressCompositeOptions, promise: P) {
let activityProgress: IDisposable;
let delayHandle: any = setTimeout(() => {
delayHandle = undefined;

const handle = this.activityService.showActivity(
viewletId,
new ProgressBadge(() => ''),
'progress-badge',
100
);

const handle = this.activityService.showActivity(viewletId, new ProgressBadge(() => ''), 'progress-badge', 100);
const startTimeVisible = Date.now();
const minTimeVisible = 300;
activityProgress = {
Expand All @@ -403,13 +430,10 @@ export class ProgressService extends Disposable implements IProgressService {
}
};
}, options.delay || 300);

promise.finally(() => {
clearTimeout(delayHandle);
dispose(activityProgress);
});

return promise;
}

private withPanelProgress<P extends Promise<R>, R = unknown>(panelid: string, task: (progress: IProgress<IProgressStep>) => P, options: IProgressCompositeOptions): P {
Expand Down
Loading