From 3ec72784c1e4b220da8b332bd3519c161042b576 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 20 Apr 2020 09:13:31 -0500 Subject: [PATCH] Notebook cell DND with nicer drag image --- .../notebook/browser/media/notebook.css | 32 ++- .../notebook/browser/notebookEditor.ts | 38 +--- .../view/renderers/backLayerWebView.ts | 8 - .../browser/view/renderers/cellRenderer.ts | 200 ++++++++++++------ .../browser/viewModel/notebookViewModel.ts | 2 +- .../notebook/test/testNotebookEditor.ts | 4 + 6 files changed, 172 insertions(+), 112 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index c34e3f81be2..d24f9f26815 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -35,11 +35,6 @@ top: 0; } -/* .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-dragover, -.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-dragover .cell-bottom-toolbar-container { - background-color: green; -} */ - .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row { cursor: default; overflow: visible !important; @@ -49,6 +44,7 @@ .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-drag-handle { position: absolute; visibility: hidden; + left: 1px; } .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.markdown-cell-row .cell-drag-handle { @@ -59,15 +55,28 @@ top: 21px; } -.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected .cell-drag-handle, .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused .cell-drag-handle, .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row:hover .cell-drag-handle { visibility: visible; } -.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-drag-image { +.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image { position: absolute; + top: -500px; z-index: 1000; + padding-bottom: 16px; +} + +.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image > .monaco-toolbar { + display: none; +} + +.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-statusbar-container { + display: none; +} + +.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-editor-part { + width: 100%; } .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell { @@ -291,7 +300,6 @@ } .monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row:hover .notebook-cell-focus-indicator, -.monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row.selected .notebook-cell-focus-indicator, .monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row.focused .notebook-cell-focus-indicator { visibility: visible; } @@ -300,6 +308,10 @@ opacity: 1; } +.monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row.cell-dragging { + opacity: 0.5; +} + .monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row .notebook-cell-insertion-indicator-top { opacity: 0; transition: opacity 0.2s ease-in-out; @@ -316,6 +328,10 @@ cursor: auto; } +.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.cell-drag-image .cell-bottom-toolbar-container { + display: none; +} + .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:focus-within, .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:hover { opacity: 1; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index f8635f07be7..751a707b45b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -28,7 +28,7 @@ import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/com import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { EditorOptions, IEditorCloseEvent, IEditorMemento } from 'vs/workbench/common/editor'; -import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_TOP_MARGIN } from 'vs/workbench/contrib/notebook/browser/constants'; +import { CELL_MARGIN, CELL_RUN_GUTTER, EDITOR_TOP_MARGIN, EDITOR_TOP_PADDING, EDITOR_BOTTOM_PADDING } from 'vs/workbench/contrib/notebook/browser/constants'; import { FoldingController } from 'vs/workbench/contrib/notebook/browser/contrib/fold/folding'; import { NotebookFindWidget } from 'vs/workbench/contrib/notebook/browser/contrib/notebookFindWidget'; import { CellEditState, CellFocusMode, ICellRange, ICellViewModel, INotebookCellList, INotebookEditor, INotebookEditorMouseEvent, NotebookLayoutInfo, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_EXECUTING_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -45,8 +45,6 @@ import { CellKind, CellUri, IOutput } from 'vs/workbench/contrib/notebook/common import { Webview } from 'vs/workbench/contrib/webview/browser/webview'; import { getExtraColor } from 'vs/workbench/contrib/welcome/walkThrough/common/walkThroughUtils'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { domEvent } from 'vs/base/browser/event'; -import { throttle } from 'vs/base/common/decorators'; const $ = DOM.$; const NOTEBOOK_EDITOR_VIEW_STATE_PREFERENCE_KEY = 'NotebookEditorViewState'; @@ -150,11 +148,6 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { return true; } - @throttle(500, (_, r) => r) - private log(msg: string) { - console.log(msg); - } - protected createEditor(parent: HTMLElement): void { this.rootElement = DOM.append(parent, $('.notebook-editor')); this.createBody(this.rootElement); @@ -237,14 +230,6 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { }, ); - domEvent(this.body, 'dragover')(e => { - // this.log('list dragover'); - }); - - domEvent(this.body, 'mousemove')(e => { - // this.log('list mousemove'); - }); - this.control = new NotebookCodeEditors(this.list, this.renderedEditors); this.webview = this.instantiationService.createInstance(BackLayerWebView, this); this._register(this.webview.onMessage(message => { @@ -253,22 +238,12 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { } })); this.list.rowsContainer.appendChild(this.webview.element); - // document.body.querySelector('.monaco-workbench')!.appendChild(this.webview!.element); this._register(this.list); // transparent cover this.webviewTransparentCover = DOM.append(this.list.rowsContainer, $('.webview-cover')); - domEvent(this.webviewTransparentCover, 'dragover')(e => { - this.log(`cover dragover`); - }); - domEvent(this.webviewTransparentCover, 'dragover')(e => { - this.log(`cover dragover`); - }); - domEvent(this.webviewTransparentCover, 'mousemove')(e => { - // this.log(`cover mousemove`); - }); - // this.webviewTransparentCover.style.display = 'none'; + this.webviewTransparentCover.style.display = 'none'; this._register(DOM.addStandardDisposableGenericMouseDownListner(this.rootElement, (e: StandardMouseEvent) => { if (DOM.hasClass(e.target, 'slider') && this.webviewTransparentCover) { @@ -279,7 +254,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { this._register(DOM.addStandardDisposableGenericMouseUpListner(this.rootElement, (e: StandardMouseEvent) => { if (this.webviewTransparentCover) { // no matter when - // this.webviewTransparentCover.style.display = 'none'; + this.webviewTransparentCover.style.display = 'none'; } })); @@ -672,7 +647,7 @@ export class NotebookEditor extends BaseEditor implements INotebookEditor { } private async moveCellToIndex(index: number, newIdx: number): Promise { - console.log(`Move ${index} to ${newIdx}`); + // console.log(`Move ${index} to ${newIdx}`); if (!this.notebookViewModel!.moveCellToIdx(index, newIdx, true)) { return; } @@ -892,6 +867,7 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .monaco-editor-background, .monaco-workbench .part.editor > .content .notebook-editor .cell .margin-view-overlays, .monaco-workbench .part.editor > .content .notebook-editor .cell .cell-statusbar-container { background: ${color}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell-drag-image .cell-editor-container > div { background: ${color} !important; }`); } const link = theme.getColor(textLinkForeground); if (link) { @@ -943,7 +919,7 @@ registerThemingParticipant((theme, collector) => { const focusedCellIndicatorColor = theme.getColor(focusedCellIndicator); if (focusedCellIndicatorColor) { collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row.focused .notebook-cell-focus-indicator { border-color: ${focusedCellIndicatorColor}; }`); - collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row.selected .notebook-cell-focus-indicator { border-color: ${focusedCellIndicatorColor}; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .notebook-cell-focus-indicator { border-color: ${focusedCellIndicatorColor}; }`); collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list-row .notebook-cell-insertion-indicator-top { background-color: ${focusedCellIndicatorColor}; }`); } @@ -953,9 +929,9 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .output { margin: 0px ${CELL_MARGIN}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px }`); collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell-bottom-toolbar-container { width: calc(100% - ${CELL_MARGIN * 2 + CELL_RUN_GUTTER}px); margin: 0px ${CELL_MARGIN}px 0px ${CELL_MARGIN + CELL_RUN_GUTTER}px }`); - collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .cell-editor-container { width: calc(100% - ${CELL_RUN_GUTTER}px); }`); collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .markdown-editor-container { margin-left: ${CELL_RUN_GUTTER}px; }`); collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > div.cell.markdown { padding-left: ${CELL_RUN_GUTTER}px; }`); collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell .run-button-container { width: ${CELL_RUN_GUTTER}px; }`); collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .monaco-list .monaco-list-row .notebook-cell-insertion-indicator-top { left: ${CELL_MARGIN + CELL_RUN_GUTTER}px; right: ${CELL_MARGIN}px; }`); + collector.addRule(`.monaco-workbench .part.editor > .content .notebook-editor .cell-drag-image .cell-editor-container > div { padding: ${EDITOR_TOP_PADDING}px 16px ${EDITOR_BOTTOM_PADDING}px 16px; }`); }); diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index b216184fe07..f3c8121062e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -108,14 +108,6 @@ export class BackLayerWebView extends Disposable { this.element.style.position = 'absolute'; this.element.style.margin = `0px 0 0px ${CELL_MARGIN}px`; - DOM.addDisposableListener(this.element, DOM.EventType.DRAG_ENTER, e => { - console.log('webview dragenter'); - }); - - DOM.addDisposableListener(this.element, DOM.EventType.DRAG_OVER, e => { - console.log('webview dragover'); - }); - const pathsPath = getPathFromAmdModule(require, 'vs/loader.js'); const loader = URI.file(pathsPath).with({ scheme: WebviewResourceScheme }); 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 ac86260a633..c1cb2ad938b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -11,13 +11,17 @@ import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/lis import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IAction, ActionRunner } from 'vs/base/common/actions'; +import { Range } from 'vs/editor/common/core/range'; import { escape } from 'vs/base/common/strings'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import * as modes from 'vs/editor/common/modes'; +import * as platform from 'vs/base/common/platform'; +import { Color } from 'vs/base/common/color'; import { deepClone } from 'vs/base/common/objects'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import * as nls from 'vs/nls'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IEditorOptions, EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenu, MenuItemAction } from 'vs/platform/actions/common/actions'; @@ -41,7 +45,8 @@ import { renderCodicons } from 'vs/base/common/codicons'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { domEvent } from 'vs/base/browser/event'; -import { throttle } from 'vs/base/common/decorators'; +import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer'; +import { ITextModel } from 'vs/editor/common/model'; const $ = DOM.$; @@ -309,10 +314,17 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR bottomCellContainer, toJSON: () => { return {}; } }; - this.dndController.addListeners(templateData); + this.dndController.addListeners(templateData, () => this.getDragImage(templateData)); return templateData; } + private getDragImage(templateData: MarkdownCellRenderTemplate): HTMLElement { + const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.markdown-cell-row'); + dragImageContainer.innerHTML = templateData.container.innerHTML; + return dragImageContainer; + } + + renderElement(element: MarkdownCellViewModel, index: number, templateData: MarkdownCellRenderTemplate, height: number | undefined): void { templateData.currentRenderedCell = element; templateData.editingContainer!.style.display = 'none'; @@ -381,6 +393,8 @@ export class MarkdownCellRenderer extends AbstractCellRenderer implements IListR } } +type DragImageProvider = () => HTMLElement; + export class CellDragAndDropController { // TODO roblou - should probably use dataTransfer here, but any dataTransfer set makes the editor think I am dropping a file, need // to figure out how to prevent that @@ -390,83 +404,158 @@ export class CellDragAndDropController { private readonly notebookEditor: INotebookEditor ) { } - addListeners(templateData: BaseCellRenderTemplate): void { + addListeners(templateData: BaseCellRenderTemplate, dragImageProvider: DragImageProvider): void { const container = templateData.container; const dragHandle = templateData.dragHandle; - const that = this; - templateData.disposables.add(domEvent(dragHandle, 'dragend')(event => { + templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_END)(() => { + // TODO (this.notebookEditor.getInnerWebview() as any)!.element.style['pointer-events'] = ''; })); - templateData.disposables.add(domEvent(dragHandle, 'drag')(event => { - // this.log(`drag`); - })); - templateData.disposables.add(domEvent(dragHandle, 'dragstart')(event => { + templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_START)(event => { (this.notebookEditor.getInnerWebview() as any)!.element.style['pointer-events'] = 'none'; if (!event.dataTransfer) { - console.log(`no datatransfer`); return; } - console.log(`onDragStart ` + !!templateData.currentRenderedCell); - that.currentDraggedCell = templateData.currentRenderedCell; - - // event.dataTransfer.setData('text/plain', 'test'); - - // let dragImage = document.body.querySelector('.cell-drag-image'); - // if (!dragImage) { - // dragImage = DOM.$('.cell-drag-image.monaco-list-row'); - // dragImage.innerHTML = container.innerHTML; - // dragImage.style.top = container.style.top; - // dragImage.style.left = '50px'; - // container.parentElement!.appendChild(dragImage); - // } - - const dragImage = DOM.$('.cell-drag-image.monaco-list-row'); - dragImage.innerHTML = container.innerHTML; - // Yikes - dragImage.style.top = container.style.top; - dragImage.style.left = '50px'; + this.currentDraggedCell = templateData.currentRenderedCell; + + const dragImage = dragImageProvider(); container.parentElement!.appendChild(dragImage); event.dataTransfer.setDragImage(dragImage, 0, 0); - setTimeout(() => container.parentElement!.removeChild(dragImage), 0); + setTimeout(() => container.parentElement!.removeChild(dragImage!), 0); // Comment this out to debug drag image layout container.classList.add('cell-dragover'); + container.classList.add('cell-dragging'); })); - templateData.disposables.add(domEvent(container, 'dragover')(event => { - event.preventDefault(); - // console.log(`dragover`); + templateData.disposables.add(domEvent(dragHandle, DOM.EventType.DRAG_END)(event => { + container.classList.remove('cell-dragging'); + })); - // this.log(event); - // this.log(`offsetY: ${event.offsetY}, clientY: ${event.clientY}, y: ${event.y}`); + templateData.disposables.add(domEvent(container, DOM.EventType.DRAG_OVER)(event => { + event.preventDefault(); })); - templateData.disposables.add(domEvent(container, 'drop')(event => { + templateData.disposables.add(domEvent(container, DOM.EventType.DROP)(event => { event.preventDefault(); - console.log(`drop`); - this.notebookEditor.moveCell(that.currentDraggedCell!, templateData.currentRenderedCell!, 'above'); + this.notebookEditor.moveCell(this.currentDraggedCell!, templateData.currentRenderedCell!, 'above'); + container.classList.remove('cell-dragover'); })); - templateData.disposables.add(domEvent(container, 'dragenter')(event => { + templateData.disposables.add(domEvent(container, DOM.EventType.DRAG_ENTER)(event => { event.preventDefault(); - // this.log('dragenter'); container.classList.add('cell-dragover'); })); - templateData.disposables.add(domEvent(container, 'dragleave')(event => { - if (event.relatedTarget && !DOM.isAncestor(event.relatedTarget as HTMLElement, container)) { + templateData.disposables.add(domEvent(container, DOM.EventType.DRAG_LEAVE)(event => { + if (!event.relatedTarget || !DOM.isAncestor(event.relatedTarget as HTMLElement, container)) { container.classList.remove('cell-dragover'); } })); } +} + +class EditorTextRenderer { + + getRichText(editor: ICodeEditor, modelRange: Range): string | null { + const model = editor.getModel(); + if (!model) { + return null; + } + + const colorMap = this._getDefaultColorMap(); + const fontInfo = editor.getOptions().get(EditorOption.fontInfo); + const fontFamily = fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily ? fontInfo.fontFamily : `'${fontInfo.fontFamily}', ${EDITOR_FONT_DEFAULTS.fontFamily}`; + + return `
` + + this._getRichTextLines(model, modelRange, colorMap) + + '
'; + } + + private _getRichTextLines(model: ITextModel, modelRange: Range, colorMap: string[]): string { + const startLineNumber = modelRange.startLineNumber; + const startColumn = modelRange.startColumn; + const endLineNumber = modelRange.endLineNumber; + const endColumn = modelRange.endColumn; + + const tabSize = model.getOptions().tabSize; + + let result = ''; + + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { + const lineTokens = model.getLineTokens(lineNumber); + const lineContent = lineTokens.getLineContent(); + const startOffset = (lineNumber === startLineNumber ? startColumn - 1 : 0); + const endOffset = (lineNumber === endLineNumber ? endColumn - 1 : lineContent.length); + + if (lineContent === '') { + result += '
'; + } else { + result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize, platform.isWindows); + } + } + + return result; + } + + private _getDefaultColorMap(): string[] { + let colorMap = modes.TokenizationRegistry.getColorMap(); + let result: string[] = ['#000000']; + if (colorMap) { + for (let i = 1, len = colorMap.length; i < len; i++) { + result[i] = Color.Format.CSS.formatHex(colorMap[i]); + } + } + return result; + } +} - @throttle(100, (_, r) => r) - private log(msg: any) { - console.log(msg); +class CodeCellDragImageRenderer { + getDragImage(templateData: CodeCellRenderTemplate): HTMLElement { + let dragImage = this._getDragImage(templateData); + if (!dragImage) { + // TODO I don't think this can happen + dragImage = document.createElement('div'); + dragImage.textContent = '1 cell'; + } + + return dragImage; + } + + private _getDragImage(templateData: CodeCellRenderTemplate): HTMLElement | null { + const dragImageContainer = DOM.$('.cell-drag-image.monaco-list-row.focused.code-cell-row'); + dragImageContainer.innerHTML = templateData.container.innerHTML; + + const editorContainer = dragImageContainer.querySelector('.cell-editor-container'); + if (!editorContainer) { + return null; + } + + const focusIndicator = dragImageContainer.querySelector('.notebook-cell-focus-indicator') as HTMLElement; + if (focusIndicator) { + focusIndicator.style.height = '40px'; + } + + const richEditorText = new EditorTextRenderer().getRichText(templateData.editor, new Range(1, 1, 1, 1000)); + if (!richEditorText) { + return null; + } + + editorContainer.innerHTML = richEditorText; + + return dragImageContainer; } } @@ -532,23 +621,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const outputContainer = DOM.append(container, $('.output')); const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); - - // domEvent(editor.getDomNode()!, 'dragover')(event => { - // event.preventDefault(); - // console.log(`dragover editor`); - // }); - - domEvent(editorContainer, 'dragover')(event => { - event.preventDefault(); - // console.log(`dragover editor container`); - }); - - domEvent(outputContainer, 'dragover')(event => { - event.preventDefault(); - // console.log(`dragover output`); - }); - - const templateData: CodeCellRenderTemplate = { insertionIndicatorTop, dragHandle, @@ -571,7 +643,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende toJSON: () => { return {}; } }; - this.dndController.addListeners(templateData); + this.dndController.addListeners(templateData, () => new CodeCellDragImageRenderer().getDragImage(templateData)); return templateData; } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts index 06e249150ad..d4086754492 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel.ts @@ -539,7 +539,7 @@ export class NotebookViewModel extends Disposable implements FoldingRegionDelega } moveCellToIdx(index: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean = true): boolean { - // TODO roblou - works differently if index > or < newIdx, write tests + // TODO roblou - works differently if index > or < newIdx, fix, write tests const viewCell = this.viewCells[index] as CellViewModel; if (!viewCell) { diff --git a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts index 6e695a50442..3ab08744882 100644 --- a/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/testNotebookEditor.ts @@ -103,6 +103,10 @@ export class TestNotebookEditor implements INotebookEditor { throw new Error('Method not implemented.'); } + moveCell(cell: ICellViewModel, relativeToCell: ICellViewModel, direction: 'above' | 'below'): Promise { + throw new Error('Method not implemented.'); + } + setSelection(cell: CellViewModel, selection: Range): void { throw new Error('Method not implemented.'); } -- GitLab