提交 2f8ddbe3 编写于 作者: B Benjamin Pasero

notifications - revised progress indication for hidden notifications (fix #91469)

上级 1b352f2f
...@@ -173,6 +173,13 @@ export interface INotificationHandle { ...@@ -173,6 +173,13 @@ export interface INotificationHandle {
*/ */
readonly onDidClose: Event<void>; readonly onDidClose: Event<void>;
/**
* Will be fired whenever the visibility of the notification changes.
* A notification can either be visible as toast or inside the notification
* center if it is visible.
*/
readonly onDidChangeVisibility: Event<boolean>;
/** /**
* Allows to indicate progress on the notification even after the * Allows to indicate progress on the notification even after the
* notification is already visible. * notification is already visible.
......
...@@ -26,10 +26,8 @@ export class ClearNotificationAction extends Action { ...@@ -26,10 +26,8 @@ export class ClearNotificationAction extends Action {
super(id, label, 'codicon-close'); super(id, label, 'codicon-close');
} }
run(notification: INotificationViewItem): Promise<any> { async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(CLEAR_NOTIFICATION, notification); this.commandService.executeCommand(CLEAR_NOTIFICATION, notification);
return Promise.resolve();
} }
} }
...@@ -46,10 +44,8 @@ export class ClearAllNotificationsAction extends Action { ...@@ -46,10 +44,8 @@ export class ClearAllNotificationsAction extends Action {
super(id, label, 'codicon-clear-all'); super(id, label, 'codicon-clear-all');
} }
run(notification: INotificationViewItem): Promise<any> { async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(CLEAR_ALL_NOTIFICATIONS); this.commandService.executeCommand(CLEAR_ALL_NOTIFICATIONS);
return Promise.resolve();
} }
} }
...@@ -66,10 +62,8 @@ export class HideNotificationsCenterAction extends Action { ...@@ -66,10 +62,8 @@ export class HideNotificationsCenterAction extends Action {
super(id, label, 'codicon-chevron-down'); super(id, label, 'codicon-chevron-down');
} }
run(notification: INotificationViewItem): Promise<any> { async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(HIDE_NOTIFICATIONS_CENTER); this.commandService.executeCommand(HIDE_NOTIFICATIONS_CENTER);
return Promise.resolve();
} }
} }
...@@ -86,10 +80,8 @@ export class ExpandNotificationAction extends Action { ...@@ -86,10 +80,8 @@ export class ExpandNotificationAction extends Action {
super(id, label, 'codicon-chevron-up'); super(id, label, 'codicon-chevron-up');
} }
run(notification: INotificationViewItem): Promise<any> { async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(EXPAND_NOTIFICATION, notification); this.commandService.executeCommand(EXPAND_NOTIFICATION, notification);
return Promise.resolve();
} }
} }
...@@ -106,10 +98,8 @@ export class CollapseNotificationAction extends Action { ...@@ -106,10 +98,8 @@ export class CollapseNotificationAction extends Action {
super(id, label, 'codicon-chevron-down'); super(id, label, 'codicon-chevron-down');
} }
run(notification: INotificationViewItem): Promise<any> { async run(notification: INotificationViewItem): Promise<any> {
this.commandService.executeCommand(COLLAPSE_NOTIFICATION, notification); this.commandService.executeCommand(COLLAPSE_NOTIFICATION, notification);
return Promise.resolve();
} }
} }
......
...@@ -100,6 +100,9 @@ export class NotificationsCenter extends Themable implements INotificationsCente ...@@ -100,6 +100,9 @@ export class NotificationsCenter extends Themable implements INotificationsCente
// Theming // Theming
this.updateStyles(); this.updateStyles();
// Mark as visible
this.model.notifications.forEach(notification => notification.updateVisibility(true));
// Context Key // Context Key
this.notificationsCenterVisibleContextKey.set(true); this.notificationsCenterVisibleContextKey.set(true);
...@@ -115,7 +118,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente ...@@ -115,7 +118,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
clearAllAction.enabled = false; clearAllAction.enabled = false;
} else { } else {
notificationsCenterTitle.textContent = localize('notifications', "Notifications"); notificationsCenterTitle.textContent = localize('notifications', "Notifications");
clearAllAction.enabled = true; clearAllAction.enabled = this.model.notifications.some(notification => !notification.hasProgress);
} }
} }
...@@ -172,20 +175,22 @@ export class NotificationsCenter extends Themable implements INotificationsCente ...@@ -172,20 +175,22 @@ export class NotificationsCenter extends Themable implements INotificationsCente
return; // only if visible return; // only if visible
} }
let focusGroup = false; let focusEditor = false;
// Update notifications list based on event // Update notifications list based on event
const [notificationsList, notificationsCenterContainer] = assertAllDefined(this.notificationsList, this.notificationsCenterContainer); const [notificationsList, notificationsCenterContainer] = assertAllDefined(this.notificationsList, this.notificationsCenterContainer);
switch (e.kind) { switch (e.kind) {
case NotificationChangeType.ADD: case NotificationChangeType.ADD:
notificationsList.updateNotificationsList(e.index, 0, [e.item]); notificationsList.updateNotificationsList(e.index, 0, [e.item]);
e.item.updateVisibility(true);
break; break;
case NotificationChangeType.CHANGE: case NotificationChangeType.CHANGE:
notificationsList.updateNotificationsList(e.index, 1, [e.item]); notificationsList.updateNotificationsList(e.index, 1, [e.item]);
break; break;
case NotificationChangeType.REMOVE: case NotificationChangeType.REMOVE:
focusGroup = isAncestor(document.activeElement, notificationsCenterContainer); focusEditor = isAncestor(document.activeElement, notificationsCenterContainer);
notificationsList.updateNotificationsList(e.index, 1); notificationsList.updateNotificationsList(e.index, 1);
e.item.updateVisibility(false);
break; break;
} }
...@@ -197,7 +202,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente ...@@ -197,7 +202,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
this.hide(); this.hide();
// Restore focus to editor group if we had focus // Restore focus to editor group if we had focus
if (focusGroup) { if (focusEditor) {
this.editorGroupService.activeGroup.focus(); this.editorGroupService.activeGroup.focus();
} }
} }
...@@ -208,13 +213,16 @@ export class NotificationsCenter extends Themable implements INotificationsCente ...@@ -208,13 +213,16 @@ export class NotificationsCenter extends Themable implements INotificationsCente
return; // already hidden return; // already hidden
} }
const focusGroup = isAncestor(document.activeElement, this.notificationsCenterContainer); const focusEditor = isAncestor(document.activeElement, this.notificationsCenterContainer);
// Hide // Hide
this._isVisible = false; this._isVisible = false;
removeClass(this.notificationsCenterContainer, 'visible'); removeClass(this.notificationsCenterContainer, 'visible');
this.notificationsList.hide(); this.notificationsList.hide();
// Mark as hidden
this.model.notifications.forEach(notification => notification.updateVisibility(false));
// Context Key // Context Key
this.notificationsCenterVisibleContextKey.set(false); this.notificationsCenterVisibleContextKey.set(false);
...@@ -222,7 +230,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente ...@@ -222,7 +230,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
this._onDidChangeVisibility.fire(); this._onDidChangeVisibility.fire();
// Restore focus to editor group if we had focus // Restore focus to editor group if we had focus
if (focusGroup) { if (focusEditor) {
this.editorGroupService.activeGroup.focus(); this.editorGroupService.activeGroup.focus();
} }
} }
......
...@@ -75,6 +75,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl ...@@ -75,6 +75,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
// Show Notifications Cneter // Show Notifications Cneter
CommandsRegistry.registerCommand(SHOW_NOTIFICATIONS_CENTER, () => { CommandsRegistry.registerCommand(SHOW_NOTIFICATIONS_CENTER, () => {
toasts.hide();
center.show(); center.show();
}); });
...@@ -92,6 +93,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl ...@@ -92,6 +93,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl
if (center.isVisible) { if (center.isVisible) {
center.hide(); center.hide();
} else { } else {
toasts.hide();
center.show(); center.show();
} }
}); });
......
...@@ -179,11 +179,8 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -179,11 +179,8 @@ export class NotificationsToasts extends Themable implements INotificationsToast
const toast: INotificationToast = { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, toDispose: itemDisposables }; const toast: INotificationToast = { item, list: notificationList, container: notificationToastContainer, toast: notificationToast, toDispose: itemDisposables };
this.mapNotificationToToast.set(item, toast); this.mapNotificationToToast.set(item, toast);
itemDisposables.add(toDisposable(() => { // When disposed, remove as visible
if (this.isToastVisible(toast) && notificationsToastsContainer) { itemDisposables.add(toDisposable(() => this.updateToastVisibility(toast, false)));
notificationsToastsContainer.removeChild(toast.container);
}
}));
// Make visible // Make visible
notificationList.show(); notificationList.show();
...@@ -236,6 +233,9 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -236,6 +233,9 @@ export class NotificationsToasts extends Themable implements INotificationsToast
addClass(notificationToast, 'notification-fade-in-done'); addClass(notificationToast, 'notification-fade-in-done');
})); }));
// Mark as visible
item.updateVisibility(true);
// Events // Events
if (!this._isVisible) { if (!this._isVisible) {
this._isVisible = true; this._isVisible = true;
...@@ -292,12 +292,13 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -292,12 +292,13 @@ export class NotificationsToasts extends Themable implements INotificationsToast
} }
private removeToast(item: INotificationViewItem): void { private removeToast(item: INotificationViewItem): void {
let focusEditor = false;
const notificationToast = this.mapNotificationToToast.get(item); const notificationToast = this.mapNotificationToToast.get(item);
let focusGroup = false;
if (notificationToast) { if (notificationToast) {
const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container); const toastHasDOMFocus = isAncestor(document.activeElement, notificationToast.container);
if (toastHasDOMFocus) { if (toastHasDOMFocus) {
focusGroup = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor focusEditor = !(this.focusNext() || this.focusPrevious()); // focus next if any, otherwise focus editor
} }
// Listeners // Listeners
...@@ -317,7 +318,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -317,7 +318,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
this.doHide(); this.doHide();
// Move focus back to editor group as needed // Move focus back to editor group as needed
if (focusGroup) { if (focusEditor) {
this.editorGroupService.activeGroup.focus(); this.editorGroupService.activeGroup.focus();
} }
} }
...@@ -346,11 +347,11 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -346,11 +347,11 @@ export class NotificationsToasts extends Themable implements INotificationsToast
} }
hide(): void { hide(): void {
const focusGroup = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false; const focusEditor = this.notificationsToastsContainer ? isAncestor(document.activeElement, this.notificationsToastsContainer) : false;
this.removeToasts(); this.removeToasts();
if (focusGroup) { if (focusEditor) {
this.editorGroupService.activeGroup.focus(); this.editorGroupService.activeGroup.focus();
} }
} }
...@@ -459,12 +460,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -459,12 +460,12 @@ export class NotificationsToasts extends Themable implements INotificationsToast
notificationToasts.push(toast); notificationToasts.push(toast);
break; break;
case ToastVisibility.HIDDEN: case ToastVisibility.HIDDEN:
if (!this.isToastVisible(toast)) { if (!this.isToastInDOM(toast)) {
notificationToasts.push(toast); notificationToasts.push(toast);
} }
break; break;
case ToastVisibility.VISIBLE: case ToastVisibility.VISIBLE:
if (this.isToastVisible(toast)) { if (this.isToastInDOM(toast)) {
notificationToasts.push(toast); notificationToasts.push(toast);
} }
break; break;
...@@ -530,7 +531,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -530,7 +531,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
// In order to measure the client height, the element cannot have display: none // In order to measure the client height, the element cannot have display: none
toast.container.style.opacity = '0'; toast.container.style.opacity = '0';
this.setVisibility(toast, true); this.updateToastVisibility(toast, true);
heightToGive -= toast.container.offsetHeight; heightToGive -= toast.container.offsetHeight;
...@@ -542,7 +543,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -542,7 +543,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
} }
// Hide or show toast based on context // Hide or show toast based on context
this.setVisibility(toast, makeVisible); this.updateToastVisibility(toast, makeVisible);
toast.container.style.opacity = ''; toast.container.style.opacity = '';
if (makeVisible) { if (makeVisible) {
...@@ -551,20 +552,24 @@ export class NotificationsToasts extends Themable implements INotificationsToast ...@@ -551,20 +552,24 @@ export class NotificationsToasts extends Themable implements INotificationsToast
}); });
} }
private setVisibility(toast: INotificationToast, visible: boolean): void { private updateToastVisibility(toast: INotificationToast, visible: boolean): void {
if (this.isToastVisible(toast) === visible) { if (this.isToastInDOM(toast) === visible) {
return; return;
} }
// Update visibility in DOM
const notificationsToastsContainer = assertIsDefined(this.notificationsToastsContainer); const notificationsToastsContainer = assertIsDefined(this.notificationsToastsContainer);
if (visible) { if (visible) {
notificationsToastsContainer.appendChild(toast.container); notificationsToastsContainer.appendChild(toast.container);
} else { } else {
notificationsToastsContainer.removeChild(toast.container); notificationsToastsContainer.removeChild(toast.container);
} }
// Update visibility in model
toast.item.updateVisibility(visible);
} }
private isToastVisible(toast: INotificationToast): boolean { private isToastInDOM(toast: INotificationToast): boolean {
return !!toast.container.parentElement; return !!toast.container.parentElement;
} }
} }
...@@ -92,6 +92,9 @@ export class NotificationHandle extends Disposable implements INotificationHandl ...@@ -92,6 +92,9 @@ export class NotificationHandle extends Disposable implements INotificationHandl
private readonly _onDidClose = this._register(new Emitter<void>()); private readonly _onDidClose = this._register(new Emitter<void>());
readonly onDidClose = this._onDidClose.event; readonly onDidClose = this._onDidClose.event;
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
constructor(private readonly item: INotificationViewItem, private readonly onClose: (item: INotificationViewItem) => void) { constructor(private readonly item: INotificationViewItem, private readonly onClose: (item: INotificationViewItem) => void) {
super(); super();
...@@ -99,6 +102,11 @@ export class NotificationHandle extends Disposable implements INotificationHandl ...@@ -99,6 +102,11 @@ export class NotificationHandle extends Disposable implements INotificationHandl
} }
private registerListeners(): void { private registerListeners(): void {
// Visibility
this._register(this.item.onDidChangeVisibility(visible => this._onDidChangeVisibility.fire(visible)));
// Closing
Event.once(this.item.onDidClose)(() => { Event.once(this.item.onDidClose)(() => {
this._onDidClose.fire(); this._onDidClose.fire();
...@@ -265,6 +273,7 @@ export interface INotificationViewItem { ...@@ -265,6 +273,7 @@ export interface INotificationViewItem {
readonly onDidChangeExpansion: Event<void>; readonly onDidChangeExpansion: Event<void>;
readonly onDidClose: Event<void>; readonly onDidClose: Event<void>;
readonly onDidChangeVisibility: Event<boolean>;
readonly onDidChangeLabel: Event<INotificationViewItemLabelChangeEvent>; readonly onDidChangeLabel: Event<INotificationViewItemLabelChangeEvent>;
expand(): void; expand(): void;
...@@ -275,6 +284,8 @@ export interface INotificationViewItem { ...@@ -275,6 +284,8 @@ export interface INotificationViewItem {
updateMessage(message: NotificationMessage): void; updateMessage(message: NotificationMessage): void;
updateActions(actions?: INotificationActions): void; updateActions(actions?: INotificationActions): void;
updateVisibility(visible: boolean): void;
close(): void; close(): void;
equals(item: INotificationViewItem): boolean; equals(item: INotificationViewItem): boolean;
...@@ -398,6 +409,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie ...@@ -398,6 +409,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private static readonly MAX_MESSAGE_LENGTH = 1000; private static readonly MAX_MESSAGE_LENGTH = 1000;
private _expanded: boolean | undefined; private _expanded: boolean | undefined;
private _visible: boolean = false;
private _actions: INotificationActions | undefined; private _actions: INotificationActions | undefined;
private _progress: NotificationViewItemProgress | undefined; private _progress: NotificationViewItemProgress | undefined;
...@@ -411,6 +423,9 @@ export class NotificationViewItem extends Disposable implements INotificationVie ...@@ -411,6 +423,9 @@ export class NotificationViewItem extends Disposable implements INotificationVie
private readonly _onDidChangeLabel = this._register(new Emitter<INotificationViewItemLabelChangeEvent>()); private readonly _onDidChangeLabel = this._register(new Emitter<INotificationViewItemLabelChangeEvent>());
readonly onDidChangeLabel = this._onDidChangeLabel.event; readonly onDidChangeLabel = this._onDidChangeLabel.event;
private readonly _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined { static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined {
if (!notification || !notification.message || isPromiseCanceledError(notification.message)) { if (!notification || !notification.message || isPromiseCanceledError(notification.message)) {
return undefined; // we need a message to show return undefined; // we need a message to show
...@@ -600,6 +615,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie ...@@ -600,6 +615,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie
this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.ACTIONS }); this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.ACTIONS });
} }
updateVisibility(visible: boolean): void {
if (this._visible !== visible) {
this._visible = visible;
this._onDidChangeVisibility.fire(visible);
}
}
expand(): void { expand(): void {
if (this._expanded || !this.canCollapse) { if (this._expanded || !this.canCollapse) {
return; return;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
import 'vs/css!./media/progressService'; import 'vs/css!./media/progressService';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IDisposable, dispose, DisposableStore, MutableDisposable, Disposable } from 'vs/base/common/lifecycle'; import { IDisposable, dispose, DisposableStore, MutableDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IProgressService, IProgressOptions, IProgressStep, ProgressLocation, IProgress, Progress, IProgressCompositeOptions, IProgressNotificationOptions, IProgressRunner, IProgressIndicator, IProgressWindowOptions } from 'vs/platform/progress/common/progress'; import { IProgressService, IProgressOptions, IProgressStep, ProgressLocation, IProgress, Progress, IProgressCompositeOptions, IProgressNotificationOptions, IProgressRunner, IProgressIndicator, IProgressWindowOptions } from 'vs/platform/progress/common/progress';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { StatusbarAlignment, IStatusbarService } from 'vs/workbench/services/statusbar/common/statusbar'; import { StatusbarAlignment, IStatusbarService } from 'vs/workbench/services/statusbar/common/statusbar';
...@@ -191,6 +191,38 @@ export class ProgressService extends Disposable implements IProgressService { ...@@ -191,6 +191,38 @@ export class ProgressService extends Disposable implements IProgressService {
} }
}; };
const createWindowProgress = () => {
// Create a promise that we can resolve as needed
// when the outside calls dispose on us
let promiseResolve: () => void;
const promise = new Promise<R>(resolve => promiseResolve = resolve);
this.withWindowProgress<R>({
location: ProgressLocation.Window,
title: options.title,
command: 'notifications.showList'
}, progress => {
// Apply any progress that was made already
if (progressStateModel.step) {
progress.report(progressStateModel.step);
}
// Continue to report progress as it happens
const onDidReportListener = progressStateModel.onDidReport(step => progress.report(step));
promise.finally(() => onDidReportListener.dispose());
// When the progress model gets disposed, we are done as well
Event.once(progressStateModel.onDispose)(() => promiseResolve());
return promise;
});
// Dispose means completing our promise
return toDisposable(() => promiseResolve());
};
const createNotification = (message: string, increment?: number): INotificationHandle => { const createNotification = (message: string, increment?: number): INotificationHandle => {
const notificationDisposables = new DisposableStore(); const notificationDisposables = new DisposableStore();
...@@ -229,7 +261,7 @@ export class ProgressService extends Disposable implements IProgressService { ...@@ -229,7 +261,7 @@ export class ProgressService extends Disposable implements IProgressService {
primaryActions.push(cancelAction); primaryActions.push(cancelAction);
} }
const handle = this.notificationService.notify({ const notification = this.notificationService.notify({
severity: Severity.Info, severity: Severity.Info,
message, message,
source: options.source, source: options.source,
...@@ -237,12 +269,26 @@ export class ProgressService extends Disposable implements IProgressService { ...@@ -237,12 +269,26 @@ export class ProgressService extends Disposable implements IProgressService {
progress: typeof increment === 'number' && increment >= 0 ? { total: 100, worked: increment } : { infinite: true } progress: typeof increment === 'number' && increment >= 0 ? { total: 100, worked: increment } : { infinite: true }
}); });
updateProgress(handle, increment); // Switch to window based progress once the notification
// changes visibility to hidden and is still ongoing.
// Remove that window based progress once the notification
// shows again.
let windowProgressDisposable: IDisposable | undefined = undefined;
notificationDisposables.add(notification.onDidChangeVisibility(visible => {
// Clear any previous running window progress
dispose(windowProgressDisposable);
// Create new window progress if notification got hidden
if (!visible && !progressStateModel.done) {
windowProgressDisposable = createWindowProgress();
}
}));
// Clear upon dispose // Clear upon dispose
Event.once(handle.onDidClose)(() => notificationDisposables.dispose()); Event.once(notification.onDidClose)(() => notificationDisposables.dispose());
return handle; return notification;
}; };
const updateProgress = (notification: INotificationHandle, increment?: number): void => { const updateProgress = (notification: INotificationHandle, increment?: number): void => {
......
...@@ -98,6 +98,17 @@ suite('Notifications', () => { ...@@ -98,6 +98,17 @@ suite('Notifications', () => {
assert.equal(called, 1); assert.equal(called, 1);
called = 0;
item1.onDidChangeVisibility(e => {
called++;
});
item1.updateVisibility(true);
item1.updateVisibility(false);
item1.updateVisibility(false);
assert.equal(called, 2);
called = 0; called = 0;
item1.onDidClose(() => { item1.onDidClose(() => {
called++; called++;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册