Skip to content

Commit

Permalink
separate listeners.
Browse files Browse the repository at this point in the history
  • Loading branch information
rebornix committed Nov 12, 2021
1 parent b335fc5 commit 0c6e106
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 50 deletions.
27 changes: 27 additions & 0 deletions src/vs/workbench/contrib/notebook/browser/notebook.layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,30 @@ Code cell outputs and markdown cells are rendered in the webview, which are asyn
However, we **don't** warmup the previous viewport as the cell height change of previous viewport might trigger the flickering of markdown cells in current viewport. Before we optimize this, do not do any warmup of cells before current viewport.



## Focus Tracking

* DOM focus tracker of notebook editor container
* focus on cell container
* focus on cell editor
* focus on cell status bar
* focus on cell/execution/output toolbar
* focus on find widget
* focus on an element in the webview/iframe
- [ ] Focus on notebook toolbar
* hasWebviewFocus


CSS
* `monaco-list.focus-within` for cell container/editor borders


* in `codecell` we update the `cell.focusMode` when rendering it and if it's not the focused element anymore, the focusMode is reverted to container


* cell editor focused and editor blur event happens
* if focused element is another cell, then change focusMode to container
* if focused element is find widget / notebookToolbar, don't change focusMode
* if focused element is webview/iframe, don't change focusMode
* if focused element is outside of the notebook, don't change focusMode
* cell editor focused and cell moves out of view (no blur event) / `CodeCell.dispose`
114 changes: 64 additions & 50 deletions src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,6 @@ export class CodeCell extends Disposable {
}
});

const updateForFocusMode = () => {
if (viewCell.focusMode === CellFocusMode.Editor && this.notebookEditor.getActiveCell() === this.viewCell) {
templateData.editor?.focus();
}

templateData.container.classList.toggle('cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor);
};
this._register(viewCell.onDidChangeState((e) => {
if (e.focusModeChanged) {
updateForFocusMode();
}
}));
updateForFocusMode();

const updateEditorOptions = () => {
const editor = templateData.editor;
if (!editor) {
Expand Down Expand Up @@ -149,32 +135,56 @@ export class CodeCell extends Disposable {
}
}));

this._register(templateData.editor.onDidContentSizeChange((e) => {
this.registerFocusModeTracker();
this.registerEditorLayoutListeners();
this.registerDecorations();
this.registerMouseListener();

// Render Outputs
this._outputContainerRenderer = this.instantiationService.createInstance(CellOutputContainer, notebookEditor, viewCell, templateData, { limit: 500 });
this._outputContainerRenderer.render(editorHeight);
// Need to do this after the intial renderOutput
if (this.viewCell.isOutputCollapsed === undefined && this.viewCell.isInputCollapsed === undefined) {
this.initialViewUpdateExpanded();
this.viewCell.layoutChange({});
}

this._register(this.viewCell.onLayoutInfoRead(() => {
this._outputContainerRenderer.probeHeight();
}));

this.updateForCollapseState();
}

private registerEditorLayoutListeners() {
this._register(this.templateData.editor.onDidContentSizeChange((e) => {
if (e.contentHeightChanged) {
if (this.viewCell.layoutInfo.editorHeight !== e.contentHeight) {
this.onCellEditorHeightChange(e.contentHeight);
}
}
}));

this._register(templateData.editor.onDidChangeCursorSelection((e) => {
this._register(this.templateData.editor.onDidChangeCursorSelection((e) => {
if (e.source === 'restoreState') {
// do not reveal the cell into view if this selection change was caused by restoring editors...
return;
}

const primarySelection = templateData.editor.getSelection();
const primarySelection = this.templateData.editor.getSelection();

if (primarySelection) {
this.notebookEditor.revealLineInViewAsync(viewCell, primarySelection.positionLineNumber);
this.notebookEditor.revealLineInViewAsync(this.viewCell, primarySelection.positionLineNumber);
}
}));
}

private registerDecorations() {
// Apply decorations
this._register(viewCell.onCellDecorationsChanged((e) => {
this._register(this.viewCell.onCellDecorationsChanged((e) => {
e.added.forEach(options => {
if (options.className) {
templateData.rootContainer.classList.add(options.className);
this.templateData.rootContainer.classList.add(options.className);
}

if (options.outputClassName) {
Expand All @@ -184,7 +194,7 @@ export class CodeCell extends Disposable {

e.removed.forEach(options => {
if (options.className) {
templateData.rootContainer.classList.remove(options.className);
this.templateData.rootContainer.classList.remove(options.className);
}

if (options.outputClassName) {
Expand All @@ -193,71 +203,75 @@ export class CodeCell extends Disposable {
});
}));

viewCell.getCellDecorations().forEach(options => {
this.viewCell.getCellDecorations().forEach(options => {
if (options.className) {
templateData.rootContainer.classList.add(options.className);
this.templateData.rootContainer.classList.add(options.className);
}

if (options.outputClassName) {
this.notebookEditor.deltaCellOutputContainerClassNames(this.viewCell.id, [options.outputClassName], []);
}
});
}

private registerMouseListener() {
// Mouse click handlers
this._register(templateData.statusBar.onDidClick(e => {
this._register(this.templateData.statusBar.onDidClick(e => {
if (e.type !== ClickTargetType.ContributedCommandItem) {
const target = templateData.editor.getTargetAtClientPoint(e.event.clientX, e.event.clientY - this.notebookEditor.notebookOptions.computeEditorStatusbarHeight(viewCell.internalMetadata));
const target = this.templateData.editor.getTargetAtClientPoint(e.event.clientX, e.event.clientY - this.notebookEditor.notebookOptions.computeEditorStatusbarHeight(this.viewCell.internalMetadata));
if (target?.position) {
templateData.editor.setPosition(target.position);
templateData.editor.focus();
this.templateData.editor.setPosition(target.position);
this.templateData.editor.focus();
}
}
}));

this._register(templateData.editor.onMouseDown(e => {
this._register(this.templateData.editor.onMouseDown(e => {
// prevent default on right mouse click, otherwise it will trigger unexpected focus changes
// the catch is, it means we don't allow customization of right button mouse down handlers other than the built in ones.
if (e.event.rightButton) {
e.event.preventDefault();
}
}));
}

private registerFocusModeTracker() {
const updateEditorForFocusModeChange = () => {
if (this.viewCell.focusMode === CellFocusMode.Editor && this.notebookEditor.getActiveCell() === this.viewCell) {
this.templateData.editor?.focus();
}

this.templateData.container.classList.toggle('cell-editor-focus', this.viewCell.focusMode === CellFocusMode.Editor);
};
this._register(this.viewCell.onDidChangeState((e) => {
if (e.focusModeChanged) {
updateEditorForFocusModeChange();
}
}));

updateEditorForFocusModeChange();

// Focus Mode
const updateFocusMode = () => {
viewCell.focusMode =
(templateData.editor.hasWidgetFocus() || (document.activeElement && this.templateData.statusBar.statusBarContainer.contains(document.activeElement)))
const updateFocusModeForEditorEvent = () => {
this.viewCell.focusMode =
(this.templateData.editor.hasWidgetFocus() || (document.activeElement && this.templateData.statusBar.statusBarContainer.contains(document.activeElement)))
? CellFocusMode.Editor
: CellFocusMode.Container;
};

this._register(templateData.editor.onDidFocusEditorWidget(() => {
updateFocusMode();
this._register(this.templateData.editor.onDidFocusEditorWidget(() => {
updateFocusModeForEditorEvent();
}));
this._register(templateData.editor.onDidBlurEditorWidget(() => {
this._register(this.templateData.editor.onDidBlurEditorWidget(() => {
// this is for a special case:
// users click the status bar empty space, which we will then focus the editor
// so we don't want to update the focus state too eagerly, it will be updated with onDidFocusEditorWidget
if (
this.notebookEditor.hasEditorFocus() &&
!(document.activeElement && this.templateData.statusBar.statusBarContainer.contains(document.activeElement))) {
updateFocusMode();
updateFocusModeForEditorEvent();
}
}));

// Render Outputs
this._outputContainerRenderer = this.instantiationService.createInstance(CellOutputContainer, notebookEditor, viewCell, templateData, { limit: 500 });
this._outputContainerRenderer.render(editorHeight);
// Need to do this after the intial renderOutput
if (this.viewCell.isOutputCollapsed === undefined && this.viewCell.isInputCollapsed === undefined) {
this.initialViewUpdateExpanded();
this.viewCell.layoutChange({});
}

this._register(this.viewCell.onLayoutInfoRead(() => {
this._outputContainerRenderer.probeHeight();
}));

this.updateForCollapseState();
}

private updateForCollapseState(): boolean {
Expand Down

0 comments on commit 0c6e106

Please sign in to comment.