collapse with ...

This commit is contained in:
Rob Lourens 2020-07-21 15:45:22 -07:00
parent 52dfde3e21
commit 13001de3b7
8 changed files with 156 additions and 59 deletions

View file

@ -26,3 +26,5 @@ export const EDITOR_TOP_PADDING = 12;
export const EDITOR_BOTTOM_PADDING = 4;
export const CELL_OUTPUT_PADDING = 14;
export const COLLAPSED_INDICATOR_HEIGHT = 40;

View file

@ -211,6 +211,15 @@
outline: none !important;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-collapsed-part {
font-size: 30px;
}
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.collapsed .cell-focus-indicator,
.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.collapsed > .monaco-toolbar {
display: none;
}
/* top and bottom borders on cells */
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-top:before,
.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.focused .cell-focus-indicator-bottom:before,

View file

@ -110,6 +110,7 @@ export interface ICellViewModel {
readonly model: NotebookCellTextModel;
readonly id: string;
readonly textBuffer: IReadonlyTextBuffer;
collapseState: CellCollapseState;
dragging: boolean;
handle: number;
uri: URI;
@ -454,6 +455,8 @@ export interface INotebookCellList {
}
export interface BaseCellRenderTemplate {
editorPart: HTMLElement;
collapsedPart: HTMLElement;
contextKeyService: IContextKeyService;
container: HTMLElement;
cellContainer: HTMLElement;
@ -471,7 +474,6 @@ export interface BaseCellRenderTemplate {
}
export interface MarkdownCellRenderTemplate extends BaseCellRenderTemplate {
editorPart: HTMLElement;
editorContainer: HTMLElement;
foldingIndicator: HTMLElement;
currentEditor?: ICodeEditor;
@ -535,6 +537,11 @@ export enum CellEditState {
Editing
}
export enum CellCollapseState {
Normal,
Collapsed
}
export enum CellFocusMode {
Container,
Editor
@ -553,6 +560,7 @@ export interface CellViewModelStateChangeEvent {
focusModeChanged?: boolean;
editStateChanged?: boolean;
languageChanged?: boolean;
collapseStateChanged?: boolean;
foldingStateChanged?: boolean;
contentChanged?: boolean;
outputIsHoveredChanged?: boolean;

View file

@ -27,7 +27,7 @@ import { contrastBorder, editorBackground, focusBorder, foreground, registerColo
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions, IEditorMemento } from 'vs/workbench/common/editor';
import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_BOTTOM_PADDING, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants';
import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_BOTTOM_PADDING, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, SCROLLABLE_ELEMENT_PADDING_TOP, BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorContribution, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_EDITOR_RUNNABLE, NOTEBOOK_HAS_MULTIPLE_KERNELS, NOTEBOOK_OUTPUT_FOCUSED, INotebookDeltaDecoration } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookEditorExtensionsRegistry } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
import { NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList';
@ -1705,6 +1705,7 @@ registerThemingParticipant((theme, collector) => {
if (focusedCellBackgroundColor) {
collector.addRule(`.notebookOverlay .code-cell-row.focused .cell-focus-indicator,
.notebookOverlay .markdown-cell-row.focused { background-color: ${focusedCellBackgroundColor} !important; }`);
collector.addRule(`.notebookOverlay .code-cell-row.focused.collapsed { background-color: ${focusedCellBackgroundColor} !important; }`);
}
const cellHoverBackgroundColor = theme.getColor(cellHoverBackground);
@ -1802,4 +1803,7 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator.cell-focus-indicator-right { width: ${CELL_MARGIN * 2}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-focus-indicator-bottom { height: ${CELL_BOTTOM_MARGIN}px; }`);
collector.addRule(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${CELL_BOTTOM_MARGIN}px; }`);
collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.collapsed { height: ${COLLAPSED_INDICATOR_HEIGHT}px; }`);
collector.addRule(`.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.collapsed .cell-collapsed-part { margin-left: ${CODE_CELL_LEFT_MARGIN + CELL_RUN_GUTTER}px; }`);
});

View file

@ -38,7 +38,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_BOTTOM_MARGIN, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CancelCellAction, ChangeCellLanguageAction, ExecuteCellAction, INotebookCellActionContext } from 'vs/workbench/contrib/notebook/browser/contrib/coreActions';
import { BaseCellRenderTemplate, CellEditState, CodeCellRenderTemplate, ICellViewModel, INotebookCellList, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { BaseCellRenderTemplate, CellCollapseState, CellEditState, CodeCellRenderTemplate, ICellViewModel, INotebookCellList, INotebookEditor, isCodeCellRenderTemplate, MarkdownCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellContextKeyManager } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellContextKeys';
import { CellMenus } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellMenus';
import { CodeCell } from 'vs/workbench/contrib/notebook/browser/view/renderers/codeCell';
@ -299,6 +299,8 @@ abstract class AbstractCellRenderer {
this.notebookEditor.selectElement(templateData.currentRenderedCell);
}
}, true));
this.setupCollapsedPart(templateData);
}
protected commonRenderElement(element: ICellViewModel, index: number, templateData: BaseCellRenderTemplate): void {
@ -308,6 +310,20 @@ abstract class AbstractCellRenderer {
templateData.container.classList.remove(DRAGGING_CLASS);
}
}
protected setupCollapsedPart(templateData: BaseCellRenderTemplate): void {
templateData.collapsedPart.textContent = '...';
DOM.hide(templateData.collapsedPart);
templateData.disposables.add(domEvent(templateData.container, DOM.EventType.DBLCLICK)(() => {
if (!templateData.currentRenderedCell) {
return;
}
templateData.currentRenderedCell.collapseState = templateData.currentRenderedCell.collapseState === CellCollapseState.Collapsed ?
CellCollapseState.Normal :
CellCollapseState.Collapsed;
}));
}
}
export class MarkdownCellRenderer extends AbstractCellRenderer implements IListRenderer<MarkdownCellViewModel, MarkdownCellRenderTemplate> {
@ -347,6 +363,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
const innerContent = DOM.append(container, $('.cell.markdown'));
const foldingIndicator = DOM.append(container, DOM.$('.notebook-folding-indicator'));
const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part'));
const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container'));
const betweenCellToolbar = disposables.add(this.createBetweenCellToolbar(bottomCellContainer, disposables, contextKeyService));
@ -354,6 +372,7 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR
const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));
const templateData: MarkdownCellRenderTemplate = {
collapsedPart,
contextKeyService,
container,
cellContainer: innerContent,
@ -926,6 +945,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
disposables.add(this.editorOptions.onDidChange(newValue => editor.updateOptions(newValue)));
const collapsedPart = DOM.append(container, $('.cell.cell-collapsed-part'));
const progressBar = new ProgressBar(editorPart);
progressBar.hide();
disposables.add(progressBar);
@ -947,6 +968,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
const titleMenu = disposables.add(this.cellMenus.getCellTitleMenu(contextKeyService));
const templateData: CodeCellRenderTemplate = {
editorPart,
collapsedPart,
contextKeyService,
container,
cellContainer,

View file

@ -11,7 +11,7 @@ import { IModeService } from 'vs/editor/common/services/modeService';
import * as nls from 'vs/nls';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { EDITOR_BOTTOM_PADDING, EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellCollapseState, CellFocusMode, CodeCellRenderTemplate, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
@ -84,14 +84,20 @@ export class CodeCell extends Disposable {
DOM.toggleClass(templateData.container, 'cell-editor-focus', viewCell.focusMode === CellFocusMode.Editor);
};
const updateForCollapseState = () => {
this.viewUpdate();
};
this._register(viewCell.onDidChangeState((e) => {
if (!e.focusModeChanged) {
return;
if (e.focusModeChanged) {
updateForFocusMode();
}
updateForFocusMode();
if (e.collapseStateChanged) {
updateForCollapseState();
}
}));
updateForFocusMode();
updateForCollapseState();
templateData.editor?.updateOptions({ readOnly: !(viewCell.getEvaluatedMetadata(notebookEditor.viewModel!.metadata).editable) });
this._register(viewCell.onDidChangeState((e) => {
@ -101,22 +107,24 @@ export class CodeCell extends Disposable {
}));
this._register(viewCell.onDidChangeState((e) => {
if (!e.languageChanged) {
return;
if (e.languageChanged) {
const mode = this._modeService.create(viewCell.language);
templateData.editor?.getModel()?.setMode(mode.languageIdentifier);
}
const mode = this._modeService.create(viewCell.language);
templateData.editor?.getModel()?.setMode(mode.languageIdentifier);
if (e.collapseStateChanged) {
// meh
this.viewCell.layoutChange({ });
this.relayoutCell();
}
}));
this._register(viewCell.onDidChangeLayout((e) => {
if (e.outerWidth === undefined) {
return;
}
const layoutInfo = templateData.editor!.getLayoutInfo();
if (layoutInfo.width !== viewCell.layoutInfo.editorWidth) {
this.onCellWidthChange();
const layoutInfo = templateData.editor!.getLayoutInfo();
if (layoutInfo.width !== viewCell.layoutInfo.editorWidth) {
this.onCellWidthChange();
}
}
}));
@ -299,6 +307,30 @@ export class CodeCell extends Disposable {
}
}
private viewUpdate(): void {
if (this.viewCell.collapseState === CellCollapseState.Collapsed) {
this.viewUpdateCollapsed();
} else {
this.viewUpdateExpanded();
}
}
private viewUpdateCollapsed(): void {
DOM.hide(this.templateData.cellContainer);
DOM.show(this.templateData.collapsedPart);
this.templateData.container.classList.toggle('collapsed', true);
this.relayoutCell();
}
private viewUpdateExpanded(): void {
DOM.show(this.templateData.cellContainer);
DOM.hide(this.templateData.collapsedPart);
this.templateData.container.classList.toggle('collapsed', false);
this.relayoutCell();
}
private layoutEditor(dimension: IDimension): void {
this.templateData.editor?.layout(dimension);
this.templateData.statusBarContainer.style.width = `${dimension.width}px`;

View file

@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@ -14,7 +13,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model';
import { SearchParams } from 'vs/editor/common/model/textModelSearch';
import { EDITOR_TOP_PADDING } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellEditState, CellFocusMode, CursorAtBoundary, CellViewModelStateChangeEvent, IEditableCellViewModel, INotebookCellDecorationOptions, CellCollapseState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { CellKind, NotebookCellMetadata, NotebookDocumentMetadata, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
@ -61,13 +60,14 @@ export abstract class BaseCellViewModel extends Disposable {
}
}
private _currentTokenSource: CancellationTokenSource | undefined;
public set currentTokenSource(v: CancellationTokenSource | undefined) {
this._currentTokenSource = v;
private _collapseState: CellCollapseState = CellCollapseState.Normal;
public get collapseState(): CellCollapseState {
return this._collapseState;
}
public get currentTokenSource(): CancellationTokenSource | undefined {
return this._currentTokenSource;
public set collapseState(v: CellCollapseState) {
this._collapseState = v;
this._onDidChangeState.fire({ collapseStateChanged: true });
}
private _focusMode: CellFocusMode = CellFocusMode.Container;

View file

@ -8,8 +8,8 @@ import * as UUID from 'vs/base/common/uuid';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as model from 'vs/editor/common/model';
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo, CodeCellLayoutState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { BOTTOM_CELL_TOOLBAR_HEIGHT, CELL_MARGIN, CELL_RUN_GUTTER, CELL_STATUSBAR_HEIGHT, EDITOR_BOTTOM_PADDING, EDITOR_TOOLBAR_HEIGHT, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, CELL_BOTTOM_MARGIN, CODE_CELL_LEFT_MARGIN, BOTTOM_CELL_TOOLBAR_OFFSET, COLLAPSED_INDICATOR_HEIGHT } from 'vs/workbench/contrib/notebook/browser/constants';
import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellViewModel, NotebookLayoutInfo, CodeCellLayoutState, CellCollapseState } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { CellKind, NotebookCellOutputsSplice, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { BaseCellViewModel } from './baseCellViewModel';
@ -101,42 +101,61 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
this._ensureOutputsTop();
const outputTotalHeight = this._outputsTop!.getTotalValue();
let newState: CodeCellLayoutState;
let editorHeight: number;
let totalHeight: number;
if (!state.editorHeight && this._layoutInfo.layoutState === CodeCellLayoutState.FromCache) {
// No new editorHeight info - keep cached totalHeight and estimate editorHeight
editorHeight = this.estimateEditorHeight(state.font?.lineHeight);
totalHeight = this._layoutInfo.totalHeight;
newState = CodeCellLayoutState.FromCache;
} else if (state.editorHeight || this._layoutInfo.layoutState === CodeCellLayoutState.Measured) {
// Editor has been measured
editorHeight = this._editorHeight;
totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight);
newState = CodeCellLayoutState.Measured;
if (this.collapseState === CellCollapseState.Normal) {
let newState: CodeCellLayoutState;
let editorHeight: number;
let totalHeight: number;
if (!state.editorHeight && this._layoutInfo.layoutState === CodeCellLayoutState.FromCache) {
// No new editorHeight info - keep cached totalHeight and estimate editorHeight
editorHeight = this.estimateEditorHeight(state.font?.lineHeight);
totalHeight = this._layoutInfo.totalHeight;
newState = CodeCellLayoutState.FromCache;
} else if (state.editorHeight || this._layoutInfo.layoutState === CodeCellLayoutState.Measured) {
// Editor has been measured
editorHeight = this._editorHeight;
totalHeight = this.computeTotalHeight(this._editorHeight, outputTotalHeight);
newState = CodeCellLayoutState.Measured;
} else {
editorHeight = this.estimateEditorHeight(state.font?.lineHeight);
totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight);
newState = CodeCellLayoutState.Estimated;
}
const indicatorHeight = editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight;
const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + EDITOR_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET;
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
this._layoutInfo = {
fontInfo: state.font || null,
editorHeight,
editorWidth,
outputContainerOffset,
outputTotalHeight,
totalHeight,
indicatorHeight,
bottomToolbarOffset,
layoutState: newState
};
} else {
editorHeight = this.estimateEditorHeight(state.font?.lineHeight);
totalHeight = this.computeTotalHeight(editorHeight, outputTotalHeight);
newState = CodeCellLayoutState.Estimated;
const indicatorHeight = COLLAPSED_INDICATOR_HEIGHT + outputTotalHeight;
const outputContainerOffset = COLLAPSED_INDICATOR_HEIGHT;
const totalHeight = COLLAPSED_INDICATOR_HEIGHT + BOTTOM_CELL_TOOLBAR_HEIGHT + CELL_BOTTOM_MARGIN;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT;
this._layoutInfo = {
fontInfo: state.font || null,
editorHeight: this._layoutInfo.editorHeight,
editorWidth: this._layoutInfo.editorWidth,
outputContainerOffset,
outputTotalHeight,
totalHeight,
indicatorHeight,
bottomToolbarOffset,
layoutState: this._layoutInfo.layoutState
};
}
const indicatorHeight = editorHeight + CELL_STATUSBAR_HEIGHT + outputTotalHeight;
const outputContainerOffset = EDITOR_TOOLBAR_HEIGHT + EDITOR_TOP_MARGIN + editorHeight + CELL_STATUSBAR_HEIGHT;
const bottomToolbarOffset = totalHeight - BOTTOM_CELL_TOOLBAR_HEIGHT - BOTTOM_CELL_TOOLBAR_OFFSET;
const editorWidth = state.outerWidth !== undefined ? this.computeEditorWidth(state.outerWidth) : this._layoutInfo?.editorWidth;
this._layoutInfo = {
fontInfo: state.font || null,
editorHeight,
editorWidth,
outputContainerOffset,
outputTotalHeight,
totalHeight,
indicatorHeight,
bottomToolbarOffset,
layoutState: newState
};
if (state.editorHeight || state.outputHeight) {
state.totalHeight = true;
}