Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork3.8k
Improve context handling#4453
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from59 commits
7a5bd0078df852db400671e298d72e93f839c7c4a892d58d038167666ede2da0ff19f64aa4de658a7a4b3fc6bde1107f6912dfc8eaf12ced55f8b32631d0e18b3b7e1db7fc2dc90b59bc3496d1f7b03da78e85c3b60aaf51a6b58d11ffaafcbb1b6a6554c24b0a32345d9d5db7ecb3c8447ab04ebac5f0ae8b29b087d1d90468ec2ae892b4ba2802f672e84357b5c0799c6c455a21d5a7b618baec7d693d72fd54ce1abda5da476064212a65027e9ef12be487bb239a56e5766427a67e4efcad221040a0bf94b1ed031011dba0b63414fd43413f40163c41c13ab63726749792693810cac907afFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -3,7 +3,7 @@ | ||
| Documents | ||
| --------- | ||
| JupyterLab can be extended inseveral ways: | ||
| - Extensions (top level): Application extensions extend the | ||
| functionality of JupyterLab itself, and we cover them in the | ||
| @@ -15,6 +15,16 @@ JupyterLab can be extended in two ways via: | ||
| For this section, the term 'document' refers to any visual thing that | ||
| is backed by a file stored on disk (i.e. uses Contents API). | ||
| Overview of document architecture | ||
| --------------------------------- | ||
| A 'document' in JupyterLab is represented by a model instance implementing the `IModel http://jupyterlab.github.io/jupyterlab/interfaces/_docregistry_src_registry_.documentregistry.imodel.html`__ interface. The model interface is intentionally fairly small, and concentrates on representing the data in the document and signaling changes to that data. Each model has an associated `context http://jupyterlab.github.io/jupyterlab/interfaces/_docregistry_src_registry_.documentregistry.icontext.html`__ instance as well. The context for a model is the bridge between the internal data of the document, stored in the model, and the file metadata and operations possible on the file, such as save and revert. Since many objects will need both the context and the model, the context contains a reference to the model as its `.model` attribute. | ||
| A single file path can have multiple different models (and hence different contexts) representing the file. For example, a notebook can be opened with a notebook model and with a text model. Models for the same file path do not directly communicate with each other. | ||
| ||
| `Document widgets http://jupyterlab.github.io/jupyterlab/interfaces/_docregistry_src_registry_.documentregistry.ireadywidget.html`__ represent a view of a document model. There can be multiple document widgets associated with a single document model, and they naturally stay in sync with each other since they are views on the same underlying data model. | ||
| The `Document | ||
| Registry <http://jupyterlab.github.io/jupyterlab/classes/_docregistry_src_registry_.documentregistry.html>`__ | ||
| is where document types and factories are registered. Plugins can | ||
| @@ -32,10 +42,10 @@ Document Registry | ||
| *Document widget extensions* in the JupyterLab application can register: | ||
| - file types | ||
| - model factories for specific file types | ||
| - widget factories for specific model factories | ||
| - widget extension factories | ||
| - file creators | ||
| `Widget Factories <http://jupyterlab.github.io/jupyterlab/classes/_docregistry_src_registry_.documentregistry.html#addwidgetfactory>`__ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -67,11 +67,12 @@ class MainAreaWidget<T extends Widget = Widget> extends Widget { | ||
| this.title.changed.connect(this._updateContentTitle, this); | ||
| content.disposed.connect(() => this.dispose()); | ||
| if (options.reveal) { | ||
| layout.addWidget(spinner); | ||
| // Make sure the ready promise is a Promise<void> to avoid leaking any | ||
| // results. | ||
| this._reveal = options.reveal.then(() => { | ||
| this._isRevealed = true; | ||
| const active = document.activeElement === spinner.node; | ||
| spinner.dispose(); | ||
| layout.addWidget(content); | ||
| @@ -88,13 +89,14 @@ class MainAreaWidget<T extends Widget = Widget> extends Widget { | ||
| BoxLayout.setStretch(error, 1); | ||
| spinner.dispose(); | ||
| layout.addWidget(error); | ||
| throw(error); | ||
| }); | ||
| // Handle no populated promise. | ||
| } else { | ||
| spinner.dispose(); | ||
| layout.addWidget(content); | ||
| this._isRevealed = true; | ||
| this._reveal = Promise.resolve(undefined); | ||
| } | ||
| } | ||
| @@ -109,16 +111,18 @@ class MainAreaWidget<T extends Widget = Widget> extends Widget { | ||
| readonly toolbar: Toolbar; | ||
| /** | ||
| * Whether the widget isrevealed. | ||
| */ | ||
| getisRevealed(): boolean { | ||
| return this._isRevealed; | ||
| } | ||
| /** | ||
| * A promise that resolves when the widget isready to be revealed. | ||
| */ | ||
| get reveal(): Promise<void> { | ||
| return this._reveal; | ||
| } | ||
| /** | ||
| * Handle the DOM events for the widget. | ||
| @@ -135,7 +139,7 @@ class MainAreaWidget<T extends Widget = Widget> extends Widget { | ||
| case 'mouseup': | ||
| case 'mouseout': | ||
| let target = event.target as HTMLElement; | ||
| if (this._isRevealed && | ||
| this.toolbar.node.contains(document.activeElement) && | ||
| target.tagName !== 'SELECT') { | ||
| this._focusContent(); | ||
| @@ -166,7 +170,7 @@ class MainAreaWidget<T extends Widget = Widget> extends Widget { | ||
| * Handle `'activate-request'` messages. | ||
| */ | ||
| protected onActivateRequest(msg: Message): void { | ||
| if (this._isRevealed) { | ||
| this._focusContent(); | ||
| } else { | ||
| this._spinner.node.focus(); | ||
| @@ -222,16 +226,21 @@ class MainAreaWidget<T extends Widget = Widget> extends Widget { | ||
| * Give focus to the content. | ||
| */ | ||
| private _focusContent(): void { | ||
| // Focus the content node if we aren't already focused on it or a | ||
| // descendent. | ||
| if (!this.content.node.contains(document.activeElement)) { | ||
| this.content.node.focus(); | ||
| } | ||
| // Activate the content asynchronously (which may change the focus). | ||
| this.content.activate(); | ||
| } | ||
| private _changeGuard = false; | ||
| private _spinner = new Spinner(); | ||
| private _isRevealed = false; | ||
| private _reveal: Promise<void>; | ||
| } | ||
| @@ -256,8 +265,37 @@ namespace MainAreaWidget { | ||
| toolbar?: Toolbar; | ||
| /** | ||
| * An optional promise for when the content is ready to be revealed. | ||
| */ | ||
| reveal?: Promise<any>; | ||
| } | ||
| /** | ||
| * An options object for main area widget subclasses providing their own | ||
| * default content. | ||
| * | ||
| * #### Notes | ||
| * This makes it easier to have a subclass that provides its own default | ||
| * content. This can go away once we upgrade to TypeScript 2.8 and have an | ||
| * easy way to make a single property optional, ala | ||
| * https://stackoverflow.com/a/46941824 | ||
| */ | ||
| export | ||
| interface IOptionsOptionalContent<T extends Widget = Widget> extends Widget.IOptions { | ||
| /** | ||
| * The child widget to wrap. | ||
| */ | ||
| content?: T; | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. What is your current thinking on ownership issues for the content of a ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. It has to transfer ownership to the MainAreaWidget. In phosphor, widgets reparent/own their children, dispose their children, move their children in the DOM to be under them, etc. | ||
| /** | ||
| * The toolbar to use for the widget. Defaults to an empty toolbar. | ||
| */ | ||
| toolbar?: Toolbar; | ||
| /** | ||
| * An optional promise for when the content is ready to be revealed. | ||
| */ | ||
| reveal?: Promise<any>; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading.Please reload this page.