diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index c64d0aa47d6..bbdaae1b8b5 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -127,7 +127,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo } } - async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata | undefined): Promise { + async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise { let controller = this._notebookProviders.get(viewType); if (controller) { @@ -236,7 +236,7 @@ export class MainThreadNotebookController implements IMainNotebookController { document?.textModel.updateLanguages(languages); } - updateNotebookMetadata(resource: UriComponents, metadata: NotebookDocumentMetadata | undefined) { + updateNotebookMetadata(resource: UriComponents, metadata: NotebookDocumentMetadata) { let document = this._mapping.get(URI.from(resource).toString()); document?.textModel.updateNotebookMetadata(metadata); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b6a7105103f..904c394eac0 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -693,7 +693,7 @@ export interface MainThreadNotebookShape extends IDisposable { $unregisterNotebookRenderer(handle: number): Promise; $createNotebookDocument(handle: number, viewType: string, resource: UriComponents): Promise; $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise; - $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata | undefined): Promise; + $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise; $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise; $spliceNotebookCells(viewType: string, resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): Promise; $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index fc42c99626c..195de3a7bf3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; @@ -17,10 +16,12 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; import { IModelDecorationsChangeAccessor, NotebookViewModel, CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel'; import { CellKind, IOutput, IRenderOutput, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NOTEBOOK_EDITABLE_CONTEXT_KEY } from 'vs/workbench/contrib/notebook/browser/constants'; export const KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED = new RawContextKey('notebookFindWidgetFocused', false); export const NOTEBOOK_EDITOR_FOCUSED = new RawContextKey('notebookEditorFocused', false); +export const NOTEBOOK_EDITOR_EDITABLE = new RawContextKey(NOTEBOOK_EDITABLE_CONTEXT_KEY, true); export interface NotebookLayoutInfo { width: number; @@ -34,11 +35,6 @@ export interface NotebookLayoutChangeEvent { fontInfo?: boolean; } -export interface NotebookViewLayoutAccessor { - layoutInfo: NotebookLayoutInfo | null; - onDidChangeLayout: Event; -} - export interface CodeCellLayoutInfo { readonly fontInfo: BareFontInfo | null; readonly editorHeight: number; @@ -52,7 +48,8 @@ export interface CodeCellLayoutChangeEvent { editorHeight?: boolean; outputHeight?: boolean; totalHeight?: boolean; - outerWidth?: boolean; + outerWidth?: number; + font?: BareFontInfo; } export interface MarkdownCellLayoutInfo { @@ -61,7 +58,8 @@ export interface MarkdownCellLayoutInfo { } export interface MarkdownCellLayoutChangeEvent { - outerWidth?: boolean; + font?: BareFontInfo; + outerWidth?: number; } export interface ICellViewModel { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index bfbc5ab913f..7bf60cc0870 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -22,7 +22,7 @@ import { contrastBorder, editorBackground, focusBorder, foreground, textBlockQuo import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions, IEditorMemento, IEditorCloseEvent } from 'vs/workbench/common/editor'; -import { INotebookEditor, NotebookLayoutInfo, CellEditState, NOTEBOOK_EDITOR_FOCUSED, CellFocusMode, ICellViewModel, CellRunState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookEditor, NotebookLayoutInfo, CellEditState, NOTEBOOK_EDITOR_FOCUSED, CellFocusMode, ICellViewModel, CellRunState, NOTEBOOK_EDITOR_EDITABLE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorInput, NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebookService'; import { OutputRenderer } from 'vs/workbench/contrib/notebook/browser/view/output/outputRenderer'; @@ -44,6 +44,7 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { Range } from 'vs/editor/common/core/range'; import { CELL_MARGIN, RUN_BUTTON_WIDTH } from 'vs/workbench/contrib/notebook/browser/constants'; import { Color, RGBA } from 'vs/base/common/color'; +import { NotebookEventDispatcher, NotebookLayoutChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; const $ = DOM.$; const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; @@ -95,6 +96,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { private list: NotebookCellList | undefined; private control: ICompositeCodeEditor | undefined; private renderedEditors: Map = new Map(); + private eventDispatcher: NotebookEventDispatcher | undefined; private notebookViewModel: NotebookViewModel | undefined; private localStore: DisposableStore = this._register(new DisposableStore()); private editorMemento: IEditorMemento; @@ -102,6 +104,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { private fontInfo: BareFontInfo | undefined; private dimension: DOM.Dimension | null = null; private editorFocus: IContextKey | null = null; + private editorEditable: IContextKey | null = null; private outputRenderer: OutputRenderer; private findWidget: NotebookFindWidget; @@ -156,6 +159,9 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { this._register(this.onDidBlur(() => { this.editorFocus?.set(false); })); + + this.editorEditable = NOTEBOOK_EDITOR_EDITABLE.bindTo(this.contextKeyService); + this.editorEditable.set(true); } private generateFontInfo(): void { @@ -175,8 +181,8 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { DOM.addClass(this.body, 'cell-list-container'); const renders = [ - this.instantiationService.createInstance(CodeCellRenderer, this, this.renderedEditors), - this.instantiationService.createInstance(MarkdownCellRenderer, this), + this.instantiationService.createInstance(CodeCellRenderer, this, this.contextKeyService, this.renderedEditors), + this.instantiationService.createInstance(MarkdownCellRenderer, this.contextKeyService, this), ]; this.list = this.instantiationService.createInstance( @@ -331,11 +337,16 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { this.list?.rowsContainer.insertAdjacentElement('afterbegin', this.webview!.element); } - this.notebookViewModel = this.instantiationService.createInstance(NotebookViewModel, input.viewType!, model); - this.notebookViewModel?.updateLayoutInfo(this.getLayoutInfo()); + this.eventDispatcher = new NotebookEventDispatcher(); + this.notebookViewModel = this.instantiationService.createInstance(NotebookViewModel, input.viewType!, model, this.eventDispatcher); + this.eventDispatcher.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); const viewState = this.loadTextEditorViewState(input); this.notebookViewModel.restoreEditorViewState(viewState); + this.localStore.add(this.eventDispatcher.onDidChangeMetadata((e) => { + this.editorEditable?.set(e.source.editable); + })); + this.localStore.add(this.notebookViewModel.onDidChangeViewCells((e) => { if (e.synchronous) { e.splices.reverse().forEach((diff) => { @@ -408,7 +419,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { DOM.toggleClass(this.rootElement, 'narrow-width', dimension.width < 600); DOM.size(this.body, dimension.width, dimension.height); this.list?.layout(dimension.height, dimension.width); - this.notebookViewModel?.updateLayoutInfo(this.getLayoutInfo()); + this.eventDispatcher?.emit([new NotebookLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); } protected saveState(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 52426a55681..6137ef0380a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -23,7 +23,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING, NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, NOTEBOOK_CELL_TYPE_CONTEXT_KEY, NOTEBOOK_EDITABLE_CONTEXT_KEY, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY } from 'vs/workbench/contrib/notebook/browser/constants'; +import { EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING, NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, NOTEBOOK_CELL_TYPE_CONTEXT_KEY, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE_CONTEXT_KEY } from 'vs/workbench/contrib/notebook/browser/constants'; import { CellRenderTemplate, CellRunState, ICellViewModel, INotebookEditor, CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus'; import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell'; @@ -155,13 +155,13 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR private disposables: Map = new Map(); constructor( + contextKeyService: IContextKeyService, notehookEditor: INotebookEditor, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IContextMenuService contextMenuService: IContextMenuService, @IKeybindingService keybindingService: IKeybindingService, @INotificationService notificationService: INotificationService, - @IContextKeyService contextKeyService: IContextKeyService, ) { super(instantiationService, notehookEditor, contextMenuService, configurationService, keybindingService, notificationService, contextKeyService, 'markdown'); } @@ -215,9 +215,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR elementDisposable.add(new StatefullMarkdownCell(this.notebookEditor, element, templateData, this.editorOptions, this.instantiationService)); const contextKeyService = this.contextKeyService.createScoped(templateData.container); - contextKeyService.createKey(NOTEBOOK_EDITABLE_CONTEXT_KEY, this.notebookEditor.viewModel?.metadata?.editable); contextKeyService.createKey(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'markdown'); - const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, element.metadata.editable); elementDisposable.add(element.onDidChangeMetadata((e) => { cellEditableKey.set(e.editable); @@ -256,13 +254,13 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende constructor( protected notebookEditor: INotebookEditor, + protected contextKeyService: IContextKeyService, private renderedEditors: Map, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, @IKeybindingService keybindingService: IKeybindingService, @INotificationService notificationService: INotificationService, - @IContextKeyService contextKeyService: IContextKeyService, ) { super(instantiationService, notebookEditor, contextMenuService, configurationService, keybindingService, notificationService, contextKeyService, 'python'); } @@ -357,9 +355,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende }; const contextKeyService = this.contextKeyService.createScoped(templateData.container); - contextKeyService.createKey(NOTEBOOK_EDITABLE_CONTEXT_KEY, this.notebookEditor.viewModel?.metadata?.editable); contextKeyService.createKey(NOTEBOOK_CELL_TYPE_CONTEXT_KEY, 'code'); - const cellEditableKey = contextKeyService.createKey(NOTEBOOK_CELL_EDITABLE_CONTEXT_KEY, element.metadata.editable); elementDisposable.add(element.onDidChangeMetadata((e) => { cellEditableKey.set(e.editable); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 185204da9a5..83b2814a5a6 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -9,9 +9,10 @@ import * as model from 'vs/editor/common/model'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer'; import { EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_PADDING, CELL_MARGIN, RUN_BUTTON_WIDTH } from 'vs/workbench/contrib/notebook/browser/constants'; -import { CellEditState, ICellViewModel, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, NotebookViewLayoutAccessor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, ICellViewModel, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind, ICell, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { BaseCellViewModel } from './baseCellViewModel'; +import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; export class CodeCellViewModel extends BaseCellViewModel implements ICellViewModel { cellKind: CellKind.Code = CellKind.Code; @@ -59,7 +60,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod readonly viewType: string, readonly notebookHandle: number, readonly cell: ICell, - private _layoutAccessor: NotebookViewLayoutAccessor, + readonly eventDispatcher: NotebookEventDispatcher, @ITextModelService private readonly _modelService: ITextModelService, ) { super(viewType, notebookHandle, cell, UUID.generateUuid()); @@ -75,7 +76,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod this._buffer = null; this._layoutInfo = { - fontInfo: _layoutAccessor.layoutInfo?.fontInfo || null, + fontInfo: null, editorHeight: 0, editorWidth: 0, outputTotalHeight: 0, @@ -83,9 +84,9 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod indicatorHeight: 0 }; - this._register(_layoutAccessor.onDidChangeLayout((e) => { - if (e.width) { - this.layoutChange({ outerWidth: true }); + this._register(eventDispatcher.onDidChangeLayout((e) => { + if (e.source.width !== undefined) { + this.layoutChange({ outerWidth: e.value.width, font: e.value.fontInfo }); } })); } @@ -98,9 +99,9 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod ? EDITOR_TOOLBAR_HEIGHT + this.editorHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING + 16 + outputTotalHeight : EDITOR_TOOLBAR_HEIGHT + this.editorHeight + EDITOR_TOP_PADDING + EDITOR_BOTTOM_PADDING + outputTotalHeight; const indicatorHeight = totalHeight - EDITOR_TOOLBAR_HEIGHT - 16; - const editorWidth = this._layoutAccessor.layoutInfo ? this._layoutAccessor.layoutInfo.width - CELL_MARGIN * 2 - RUN_BUTTON_WIDTH : 0; + const editorWidth = state.outerWidth !== undefined ? state.outerWidth - CELL_MARGIN * 2 - RUN_BUTTON_WIDTH : 0; this._layoutInfo = { - fontInfo: this._layoutAccessor.layoutInfo?.fontInfo || null, + fontInfo: state.font || null, editorHeight: this._editorHeight, editorWidth, outputTotalHeight, diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts new file mode 100644 index 00000000000..e67499fa1d6 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from 'vs/base/common/event'; +import { NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookLayoutChangeEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; + +export enum NotebookViewEventType { + LayoutChanged = 1, + MetadataChanged = 2 +} + +export class NotebookLayoutChangedEvent { + public readonly type = NotebookViewEventType.LayoutChanged; + + constructor(readonly source: NotebookLayoutChangeEvent, readonly value: NotebookLayoutInfo) { + + } +} + + +export class NotebookMetadataChangedEvent { + public readonly type = NotebookViewEventType.MetadataChanged; + + constructor(readonly source: NotebookDocumentMetadata) { + + } +} + + +export type NotebookViewEvent = NotebookLayoutChangedEvent | NotebookMetadataChangedEvent; + +export class NotebookEventDispatcher { + protected readonly _onDidChangeLayout = new Emitter(); + readonly onDidChangeLayout = this._onDidChangeLayout.event; + protected readonly _onDidChangeMetadata = new Emitter(); + readonly onDidChangeMetadata = this._onDidChangeMetadata.event; + + constructor() { + } + + emit(events: NotebookViewEvent[]) { + for (let i = 0, len = events.length; i < len; i++) { + let e = events[i]; + + switch (e.type) { + case NotebookViewEventType.LayoutChanged: + this._onDidChangeLayout.fire(e); + break; + case NotebookViewEventType.MetadataChanged: + this._onDidChangeMetadata.fire(e); + break; + } + } + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index 3a639e9d5c6..33dfdae7ea5 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -8,11 +8,12 @@ import * as UUID from 'vs/base/common/uuid'; import * as model from 'vs/editor/common/model'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ICellViewModel, CellFindMatch, NotebookViewLayoutAccessor, MarkdownCellLayoutInfo, MarkdownCellLayoutChangeEvent, CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { ICellViewModel, CellFindMatch, MarkdownCellLayoutInfo, MarkdownCellLayoutChangeEvent, CellEditState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { MarkdownRenderer } from 'vs/workbench/contrib/notebook/browser/view/renderers/mdRenderer'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; import { CellKind, ICell } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CELL_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; +import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; export class MarkdownCellViewModel extends BaseCellViewModel implements ICellViewModel { cellKind: CellKind.Markdown = CellKind.Markdown; @@ -34,29 +35,29 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie readonly viewType: string, readonly notebookHandle: number, readonly cell: ICell, - private _layoutAccessor: NotebookViewLayoutAccessor, + readonly eventDispatcher: NotebookEventDispatcher, @IInstantiationService private readonly _instaService: IInstantiationService, @ITextModelService private readonly _modelService: ITextModelService) { super(viewType, notebookHandle, cell, UUID.generateUuid()); this._layoutInfo = { - fontInfo: this._layoutAccessor.layoutInfo?.fontInfo || null, + fontInfo: null, editorWidth: 0 }; - this._register(_layoutAccessor.onDidChangeLayout((e) => { - if (e.width) { - this.layoutChange({ outerWidth: true }); + this._register(eventDispatcher.onDidChangeLayout((e) => { + if (e.source.width || e.source.fontInfo) { + this.layoutChange({ outerWidth: e.value.width, font: e.value.fontInfo }); } })); } layoutChange(state: MarkdownCellLayoutChangeEvent) { // recompute - const editorWidth = this._layoutAccessor.layoutInfo ? this._layoutAccessor.layoutInfo.width - CELL_MARGIN * 2 : 0; + const editorWidth = state.outerWidth !== undefined ? state.outerWidth - CELL_MARGIN * 2 : 0; this._layoutInfo = { - fontInfo: this._layoutAccessor.layoutInfo?.fontInfo || null, + fontInfo: state.font || null, editorWidth }; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 8b7943e1cf6..8acd6c15f1e 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -14,12 +14,13 @@ import { IModelDeltaDecoration } from 'vs/editor/common/model'; import { WorkspaceTextEdit } from 'vs/editor/common/modes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { CellFindMatch, CellEditState, ICellViewModel, NotebookLayoutChangeEvent, NotebookViewLayoutAccessor, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellFindMatch, CellEditState, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookEditorModel } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput'; import { DeleteCellEdit, InsertCellEdit, MoveCellEdit } from 'vs/workbench/contrib/notebook/browser/viewModel/cellEdit'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkdownCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel'; import { CellKind, ICell } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookEventDispatcher, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; export interface INotebookEditorViewState { editingCells: { [key: number]: boolean }; @@ -54,7 +55,7 @@ export interface INotebookViewCellsUpdateEvent { splices: NotebookViewCellsSplice[]; } -export class NotebookViewModel extends Disposable implements NotebookViewLayoutAccessor { +export class NotebookViewModel extends Disposable { private _localStore: DisposableStore = this._register(new DisposableStore()); private _viewCells: CellViewModel[] = []; @@ -98,16 +99,10 @@ export class NotebookViewModel extends Disposable implements NotebookViewLayoutA return null; } - protected readonly _onDidChangeLayout = new Emitter(); - readonly onDidChangeLayout = this._onDidChangeLayout.event; - private _layoutInfo: NotebookLayoutInfo | null = null; - get layoutInfo() { - return this._layoutInfo; - } - constructor( public viewType: string, private _model: NotebookEditorModel, + readonly eventDispatcher: NotebookEventDispatcher, @IInstantiationService private readonly instantiationService: IInstantiationService, @IBulkEditService private readonly bulkEditService: IBulkEditService, @IUndoRedoService private readonly undoService: IUndoRedoService @@ -125,6 +120,10 @@ export class NotebookViewModel extends Disposable implements NotebookViewLayoutA }); })); + this._register(this._model.notebook.onDidChangeMetadata(e => { + this.eventDispatcher.emit([new NotebookMetadataChangedEvent(e)]); + })); + this._viewCells = this._model!.notebook!.cells.map(cell => { return createCellViewModel(this.instantiationService, this, cell); }); @@ -383,24 +382,6 @@ export class NotebookViewModel extends Disposable implements NotebookViewLayoutA return this._model === model; } - updateLayoutInfo(layoutInfo: NotebookLayoutInfo | null) { - if (layoutInfo === null) { - return; - } - - if (this._layoutInfo === null) { - this._layoutInfo = layoutInfo; - this._onDidChangeLayout.fire({ width: true, height: true }); - return; - } - - let widthChanged = layoutInfo.width !== this._layoutInfo.width; - let heightChanged = layoutInfo.height !== this._layoutInfo.height; - - this._layoutInfo = layoutInfo; - this._onDidChangeLayout.fire({ width: widthChanged, height: heightChanged }); - } - dispose() { this._localStore.clear(); this._viewCells.forEach(cell => { @@ -416,8 +397,8 @@ export type CellViewModel = CodeCellViewModel | MarkdownCellViewModel; export function createCellViewModel(instantiationService: IInstantiationService, notebookViewModel: NotebookViewModel, cell: ICell) { if (cell.cellKind === CellKind.Code) { - return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, notebookViewModel.handle, cell, notebookViewModel); + return instantiationService.createInstance(CodeCellViewModel, notebookViewModel.viewType, notebookViewModel.handle, cell, notebookViewModel.eventDispatcher); } else { - return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, notebookViewModel.handle, cell, notebookViewModel); + return instantiationService.createInstance(MarkdownCellViewModel, notebookViewModel.viewType, notebookViewModel.handle, cell, notebookViewModel.eventDispatcher); } } diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index b03f15a120a..c2945fc41fd 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -16,6 +16,8 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel get onDidChangeCells(): Event { return this._onDidChangeCells.event; } private _onDidChangeContent = new Emitter(); onDidChangeContent: Event = this._onDidChangeContent.event; + private _onDidChangeMetadata = new Emitter(); + onDidChangeMetadata: Event = this._onDidChangeMetadata.event; private _mapping: Map = new Map(); private _cellListeners: Map = new Map(); cells: NotebookCellTextModel[]; @@ -36,8 +38,9 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel this.languages = languages; } - updateNotebookMetadata(metadata: NotebookDocumentMetadata | undefined) { + updateNotebookMetadata(metadata: NotebookDocumentMetadata) { this.metadata = metadata; + this._onDidChangeMetadata.fire(this.metadata); } updateNotebookCellMetadata(handle: number, metadata: NotebookCellMetadata) { diff --git a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts index 5b482c2a7f3..36738cb64a4 100644 --- a/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/notebookViewModel.test.ts @@ -13,6 +13,7 @@ import { withTestNotebook, TestCell } from 'vs/workbench/contrib/notebook/test/t import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; suite('NotebookViewModel', () => { const instantiationService = new TestInstantiationService(); @@ -23,7 +24,8 @@ suite('NotebookViewModel', () => { test('ctor', function () { const notebook = new NotebookTextModel(0, 'notebook', URI.parse('test')); const model = new NotebookEditorModel(notebook); - const viewModel = new NotebookViewModel('notebook', model, instantiationService, blukEditService, undoRedoService); + const eventDispatcher = new NotebookEventDispatcher(); + const viewModel = new NotebookViewModel('notebook', model, eventDispatcher, instantiationService, blukEditService, undoRedoService); assert.equal(viewModel.viewType, 'notebook'); }); diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 50ad64d4e17..ed4e270a817 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -19,6 +19,7 @@ import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; +import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher'; export class TestCell implements ICell { uri: URI; @@ -202,7 +203,8 @@ export function withTestNotebook(instantiationService: IInstantiationService, bl return new NotebookCellTextModel(notebook.uri, index, cell[0], cell[1], cell[2], cell[3], cell[4]); }); const model = new NotebookEditorModel(notebook); - const viewModel = new NotebookViewModel(viewType, model, instantiationService, blukEditService, undoRedoService); + const eventDispatcher = new NotebookEventDispatcher(); + const viewModel = new NotebookViewModel(viewType, model, eventDispatcher, instantiationService, blukEditService, undoRedoService); callback(editor, viewModel);