Skip to content

Commit

Permalink
Update API surface area, names, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
afshin authored and foo committed Oct 19, 2022
1 parent d04aae0 commit 1a86b74
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 102 deletions.
127 changes: 54 additions & 73 deletions packages/application/src/layoutrestorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ export interface ILayoutRestorer extends IRestorer {
restored: Promise<void>;

/**
* A boolean whether some tracker are still waiting for restoration.
* Useful in case the application has started in 'single-document' mode
* and the main area has not been restored.
* Whether full layout restoration is deferred and is currently incomplete.
*
* #### Notes
* This flag is useful for tracking when the application has started in
* 'single-document' mode and the main area has not yet been restored.
*/
hasUnrestoredTracker: boolean;
isDeferred: boolean;

/**
* Add a widget to be tracked by the layout restorer.
Expand Down Expand Up @@ -75,7 +77,7 @@ export interface ILayoutRestorer extends IRestorer {
*
* @returns - the rehydrated main area.
*/
restoreDelayed(): Promise<ILabShell.IMainArea | null>;
restoreDeferred(): Promise<ILabShell.IMainArea | null>;
}

/**
Expand Down Expand Up @@ -161,19 +163,21 @@ export class LayoutRestorer implements ILayoutRestorer {
}

/**
* A promise resolved when the layout restorer is ready to receive signals.
* Whether full layout restoration is deferred and is currently incomplete.
*
* #### Notes
* This flag is useful for tracking when the application has started in
* 'single-document' mode and the main area has not yet been restored.
*/
get restored(): Promise<void> {
return this._restored.promise;
get isDeferred(): boolean {
return this._deferred.length > 0;
}

/**
* A boolean whether some tracker are still waiting for restoration.
* Useful in case the application has started in 'single-document' mode
* and main area has not been restored.
* A promise resolved when the layout restorer is ready to receive signals.
*/
get hasUnrestoredTracker(): boolean {
return this._unrestoredTrackers.length > 0;
get restored(): Promise<void> {
return this._restored.promise;
}

/**
Expand Down Expand Up @@ -222,7 +226,7 @@ export class LayoutRestorer implements ILayoutRestorer {
if (this._mode === 'multiple-document') {
mainArea = this._rehydrateMainArea(main);
} else {
this._delayedMainAreaLayout = main;
this._deferredMainArea = main;
}

// Rehydrate down area.
Expand Down Expand Up @@ -255,22 +259,17 @@ export class LayoutRestorer implements ILayoutRestorer {
*
* @param options - The restoration options.
*/
restore(
async restore(
tracker: WidgetTracker,
options: IRestorer.IOptions<Widget>
): Promise<any> {
const warning = 'restore() can only be called before `first` has resolved.';

if (this._firstDone) {
console.warn(warning);
return Promise.reject(warning);
throw new Error('restore() must be called before `first` has resolved.');
}

const { namespace } = tracker;
if (this._trackers.has(namespace)) {
const warning = `A tracker namespaced ${namespace} was already restored.`;
console.warn(warning);
return Promise.reject(warning);
throw new Error(`The tracker "${namespace}" is already restored.`);
}

const { args, command, name, when } = options;
Expand Down Expand Up @@ -314,57 +313,43 @@ export class LayoutRestorer implements ILayoutRestorer {
this._promises.push(promise);

return promise;
} else {
tracker.delayedRestore({
args: args || (() => JSONExt.emptyObject),
command,
connector: this._connector,
name,
registry: this._registry,
when: when ? [first].concat(when) : first
});

this._unrestoredTrackers.push(tracker);
return Promise.resolve();
}

tracker.defer({
args: args || (() => JSONExt.emptyObject),
command,
connector: this._connector,
name,
registry: this._registry,
when: when ? [first].concat(when) : first
});
this._deferred.push(tracker);
}

/**
* Restore the main area layout on demand.
* This happens when the application has started in 'single-document' mode
* (no main area widget loaded) and is switching to 'multiple-document' mode.
* Restore the application layout if its restoration has been deferred.
*
* @returns - the rehydrated main area.
*/
async restoreDelayed(): Promise<ILabShell.IMainArea | null> {
const promises = new Array<Promise<any>>();
async restoreDeferred(): Promise<ILabShell.IMainArea | null> {
if (!this.isDeferred) {
return null;
}

// Restore all the main area widgets.
this._unrestoredTrackers.forEach(widgetsTracker =>
promises.push(widgetsTracker.restore())
);
// Empty the deferred list and wait for all trackers to restore.
const wait = Promise.resolve();
const promises = this._deferred.map(t => wait.then(() => t.restore()));
this._deferred.length = 0;
await Promise.all(promises);

// Rehydrate the main area layout.
let mainArea: ILabShell.IMainArea | null = null;
await Promise.all(promises)
.then(() => {
this._unrestoredTrackers.length = 0;
if (this._delayedMainAreaLayout) {
mainArea = this._rehydrateMainArea(this._delayedMainAreaLayout);
}
})
.catch(reason => {
console.error('Fail to rehydrate the main area.');
console.error(reason);
});

return mainArea;
return this._rehydrateMainArea(this._deferredMainArea);
}

/**
* Save the layout state for the application.
*/
save(data: ILabShell.ILayout): Promise<void> {
save(layout: ILabShell.ILayout): Promise<void> {
// If there are promises that are unresolved, bail.
if (!this._promisesDone) {
const warning = 'save() was called prematurely.';
Expand All @@ -374,19 +359,15 @@ export class LayoutRestorer implements ILayoutRestorer {

const dehydrated: Private.ILayout = {};

// Save the backup dehydrated main area layout if the main area has not been
// restored (application stated in 'single-document' mode).
if (!this.hasUnrestoredTracker) {
dehydrated.main = this._dehydrateMainArea(data.mainArea);
} else {
dehydrated.main = this._delayedMainAreaLayout;
}

dehydrated.down = this._dehydrateDownArea(data.downArea);
dehydrated.left = this._dehydrateSideArea(data.leftArea);
dehydrated.right = this._dehydrateSideArea(data.rightArea);
dehydrated.relativeSizes = data.relativeSizes;
dehydrated.top = { ...data.topArea };
// Save the cached main area layout if restoration is deferred.
dehydrated.main = this.isDeferred
? this._deferredMainArea
: this._dehydrateMainArea(layout.mainArea);
dehydrated.down = this._dehydrateDownArea(layout.downArea);
dehydrated.left = this._dehydrateSideArea(layout.leftArea);
dehydrated.right = this._dehydrateSideArea(layout.rightArea);
dehydrated.relativeSizes = layout.relativeSizes;
dehydrated.top = { ...layout.topArea };

return this._connector.save(KEY, dehydrated);
}
Expand Down Expand Up @@ -557,6 +538,8 @@ export class LayoutRestorer implements ILayoutRestorer {
}

private _connector: IDataConnector<ReadonlyPartialJSONValue>;
private _deferred = new Array<WidgetTracker>();
private _deferredMainArea?: Private.IMainArea | null = null;
private _first: Promise<any>;
private _firstDone = false;
private _promisesDone = false;
Expand All @@ -566,8 +549,6 @@ export class LayoutRestorer implements ILayoutRestorer {
private _trackers = new Set<string>();
private _widgets = new Map<string, Widget>();
private _mode: DockPanel.Mode = 'multiple-document';
private _unrestoredTrackers = new Array<WidgetTracker>();
private _delayedMainAreaLayout: Private.IMainArea | null | undefined = null;
}

/**
Expand Down
22 changes: 7 additions & 15 deletions packages/application/src/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,19 +602,18 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell {
const widgets = Array.from(dock.widgets());
dock.mode = mode;

// Restore the original layout.
// Restore cached layout if possible.
if (this._cachedLayout) {
// Remove any disposed widgets in the cached layout and restore.
Private.normalizeAreaConfig(dock, this._cachedLayout.main);
dock.restoreLayout(this._cachedLayout);
this._cachedLayout = null;
}

// Restore the widgets not restored in first place because the shell
// started in 'single-document' mode.
if (this._layoutRestorer) {
// If layout restoration has been deferred, restore layout now.
if (this._layoutRestorer.isDeferred) {
this._layoutRestorer
.restoreDelayed()
.restoreDeferred()
.then(mainArea => {
if (mainArea) {
const { currentWidget, dock } = mainArea;
Expand All @@ -627,10 +626,9 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell {
}
})
.catch(reason => {
console.error('Fail to restore the layout delayed.');
console.error('Failed to restore the deferred layout.');
console.error(reason);
});
this._layoutRestorer = null;
}

// Add any widgets created during single document mode, which have
Expand Down Expand Up @@ -1078,17 +1076,11 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell {
this.add(widget, area, options);
});
this._delayedWidget.length = 0;
this._layoutRestorer = layoutRestorer;

// Get the layout from the restorer
const layout = await layoutRestorer.fetch();

// Keep the restorer if some tracker has not been restored yet.
// Typically is the application has started in 'single-document' mode,
// and the main area has not been restored.
if (layoutRestorer.hasUnrestoredTracker) {
this._layoutRestorer = layoutRestorer;
}

// Reset the layout
const { mainArea, downArea, leftArea, rightArea, topArea, relativeSizes } =
layout;
Expand Down Expand Up @@ -1725,7 +1717,7 @@ export class LabShell extends Widget implements JupyterFrontEnd.IShell {
};
private _delayedWidget = new Array<ILabShell.IDelayedWidget>();
private _translator: ITranslator;
private _layoutRestorer: ILayoutRestorer | null = null;
private _layoutRestorer: ILayoutRestorer;
}

namespace Private {
Expand Down
27 changes: 13 additions & 14 deletions packages/apputils/src/widgettracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export class WidgetTracker<T extends Widget = Widget>
* A promise resolved when the tracker has been restored.
*/
get restored(): Promise<void> {
if (this._delayedRestore) {
if (this._deferred) {
return Promise.resolve();
} else {
return this._pool.restored;
Expand Down Expand Up @@ -321,17 +321,16 @@ export class WidgetTracker<T extends Widget = Widget>
* This function should not typically be invoked by client code.
* Its primary use case is to be invoked by a restorer.
*/
async restore(options: IRestorable.IOptions<T> | void): Promise<any> {
let promise = null;
if (this._delayedRestore) {
promise = this._pool.restore(this._delayedRestore);
this._delayedRestore = null;
} else if (options) {
promise = this._pool.restore(options);
} else {
console.warn('No options provided to restore the tracker.');
async restore(options?: IRestorable.IOptions<T>): Promise<any> {
const deferred = this._deferred;
if (deferred) {
this._deferred = null;
return this._pool.restore(deferred);
}
if (options) {
return this._pool.restore(options);
}
return promise;
console.warn('No options provided to restore the tracker.');
}

/**
Expand All @@ -343,8 +342,8 @@ export class WidgetTracker<T extends Widget = Widget>
* This function is useful when starting the shell in 'single-document' mode,
* to avoid restoring all useless widgets.
*/
delayedRestore(options: IRestorable.IOptions<T>): void {
this._delayedRestore = options;
defer(options: IRestorable.IOptions<T>): void {
this._deferred = options;
}

/**
Expand All @@ -367,12 +366,12 @@ export class WidgetTracker<T extends Widget = Widget>
}

private _currentChanged = new Signal<this, T | null>(this);
private _deferred: IRestorable.IOptions<T> | null = null;
private _focusTracker: FocusTracker<T>;
private _pool: RestorablePool<T>;
private _isDisposed = false;
private _widgetAdded = new Signal<this, T>(this);
private _widgetUpdated = new Signal<this, T>(this);
private _delayedRestore: IRestorable.IOptions<T> | null = null;
}

/**
Expand Down

0 comments on commit 1a86b74

Please sign in to comment.