diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 13dd8dc5e13..452e1f078e9 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -23,6 +23,7 @@ import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/no import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { CodiconActionViewItem } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView'; import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart'; +import { registerStickyScroll } from 'vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll'; import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; export class BetweenCellToolbar extends CellPart { @@ -112,12 +113,14 @@ export class CellTitleToolbarPart extends CellPart { private readonly _onDidUpdateActions: Emitter = this._register(new Emitter()); readonly onDidUpdateActions: Event = this._onDidUpdateActions.event; + private cellDisposable = this._register(new DisposableStore()); + get hasActions(): boolean { return this._hasActions; } constructor( - toolbarContainer: HTMLElement, + private readonly toolbarContainer: HTMLElement, private readonly _rootClassDelegate: ICssClassDelegate, toolbarId: MenuId, private readonly _notebookEditor: INotebookEditorDelegate, @@ -139,6 +142,9 @@ export class CellTitleToolbarPart extends CellPart { } renderCell(element: ICellViewModel, templateData: BaseCellRenderTemplate): void { + this.cellDisposable.clear(); + this.cellDisposable.add(registerStickyScroll(this._notebookEditor, element, this.toolbarContainer, { extraOffset: 4, min: -14 })); + this.updateContext({ ui: true, cell: element, diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts index d40a601703e..44447f75622 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts @@ -7,7 +7,6 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { Action, IAction } from 'vs/base/common/actions'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { MarshalledId } from 'vs/base/common/marshalling'; -import { clamp } from 'vs/base/common/numbers'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize } from 'vs/nls'; import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; @@ -22,13 +21,14 @@ import { INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browse import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellPart'; +import { registerStickyScroll } from 'vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll'; import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; export class RunToolbar extends CellPart { private toolbar!: ToolBar; - private currentElement: ICellViewModel | undefined; + private cellDisposable = this._register(new DisposableStore()); constructor( readonly notebookEditor: INotebookEditorDelegate, @@ -42,8 +42,6 @@ export class RunToolbar extends CellPart { ) { super(); - this.notebookEditor.onDidScroll(() => this.updateForScroll()); - const menu = this._register(menuService.createMenu(this.notebookEditor.creationOptions.menuIds.cellExecutePrimary!, contextKeyService)); this.createRunCellToolbar(runButtonContainer, cellContainer, contextKeyService); const updateActions = () => { @@ -56,27 +54,10 @@ export class RunToolbar extends CellPart { this._register(this.notebookEditor.notebookOptions.onDidChangeOptions(updateActions)); } - private updateForScroll() { - if (this.currentElement) { - if (this.currentElement.isInputCollapsed) { - this.runButtonContainer.style.top = ''; - } else { - const scrollTop = this.notebookEditor.scrollTop; - const elementTop = this.notebookEditor.getAbsoluteTopOfElement(this.currentElement); - const scrollPadding = this.notebookEditor.notebookOptions.computeTopInsertToolbarHeight(this.notebookEditor.textModel?.viewType); - const diff = scrollTop - scrollPadding - elementTop; - const maxTop = this.currentElement.layoutInfo.editorHeight + this.currentElement.layoutInfo.statusBarHeight - 45; // subtract roughly the height of the execution order label plus padding - const top = maxTop > 20 ? // Don't move the run button if it can only move a very short distance - clamp(0, diff, maxTop) : - 0; - this.runButtonContainer.style.top = `${top}px`; - } - } - } - renderCell(element: ICellViewModel, templateData: BaseCellRenderTemplate): void { - this.currentElement = element; - this.updateForScroll(); + this.cellDisposable.clear(); + this.cellDisposable.add(registerStickyScroll(this.notebookEditor, element, this.runButtonContainer)); + this.toolbar.context = { ui: true, cell: element, diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll.ts new file mode 100644 index 00000000000..2ce43c214ba --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/stickyScroll.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable } from 'vs/base/common/lifecycle'; +import { clamp } from 'vs/base/common/numbers'; +import { ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; + +export function registerStickyScroll(notebookEditor: INotebookEditor, cell: ICellViewModel, element: HTMLElement, opts?: { extraOffset?: number; min?: number }): IDisposable { + const extraOffset = opts?.extraOffset ?? 0; + const min = opts?.min ?? 0; + + const updateForScroll = () => { + if (cell.isInputCollapsed) { + element.style.top = ''; + } else { + const scrollPadding = notebookEditor.notebookOptions.computeTopInsertToolbarHeight(notebookEditor.textModel?.viewType); + const scrollTop = notebookEditor.scrollTop - scrollPadding; + const elementTop = notebookEditor.getAbsoluteTopOfElement(cell); + const diff = scrollTop - elementTop + extraOffset; + const maxTop = cell.layoutInfo.editorHeight + cell.layoutInfo.statusBarHeight - 45; // subtract roughly the height of the execution order label plus padding + const top = maxTop > 20 ? // Don't move the run button if it can only move a very short distance + clamp(min, diff, maxTop) : + 0; + element.style.top = `${top}px`; + } + }; + + updateForScroll(); + return notebookEditor.onDidScroll(() => updateForScroll()); +}