panelview: dnd

This commit is contained in:
Joao Moreno
2017-09-17 16:59:11 +02:00
parent 18715a07e3
commit d522fb67cf

View File

@@ -6,13 +6,14 @@
'use strict';
import 'vs/css!./splitview';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
import Event, { Emitter, chain } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { $, append, addClass, removeClass, toggleClass } from 'vs/base/browser/dom';
import { firstIndex } from 'vs/base/common/arrays';
import { Color } from 'vs/base/common/color';
import { SplitView, IView } from './splitview2';
enum PanelState {
@@ -36,7 +37,7 @@ export abstract class Panel implements IView {
private _minimumBodySize: number;
private _maximumBodySize: number;
private ariaHeaderLabel: string;
private header: HTMLElement;
readonly header: HTMLElement;
private disposables: IDisposable[] = [];
get minimumBodySize(): number {
@@ -155,6 +156,95 @@ export abstract class Panel implements IView {
}
}
interface IDndContext {
draggable: PanelDraggable | null;
}
class PanelDraggable implements IDisposable {
// see https://github.com/Microsoft/vscode/issues/14470
private dragOverCounter = 0;
private dropBackground: Color | undefined;
private disposables: IDisposable[] = [];
private _onDidDrop = new Emitter<{ from: Panel, to: Panel }>();
readonly onDidDrop = this._onDidDrop.event;
constructor(private panel: Panel, private context: IDndContext) {
domEvent(panel.header, 'dragstart')(this.onDragStart, this, this.disposables);
domEvent(panel.header, 'dragenter')(this.onDragEnter, this, this.disposables);
domEvent(panel.header, 'dragleave')(this.onDragLeave, this, this.disposables);
domEvent(panel.header, 'dragend')(this.onDragEnd, this, this.disposables);
domEvent(panel.header, 'drop')(this.onDrop, this, this.disposables);
}
private onDragStart(e: DragEvent): void {
e.dataTransfer.effectAllowed = 'move';
const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.header.textContent));
e.dataTransfer.setDragImage(dragImage, -10, -10);
setTimeout(() => document.body.removeChild(dragImage), 0);
this.context.draggable = this;
}
private onDragEnter(e: DragEvent): void {
if (!this.context.draggable || this.context.draggable === this) {
return;
}
this.dragOverCounter++;
this.renderHeader();
}
private onDragLeave(e: DragEvent): void {
if (!this.context.draggable || this.context.draggable === this) {
return;
}
this.dragOverCounter--;
if (this.dragOverCounter === 0) {
this.renderHeader();
}
}
private onDragEnd(e: DragEvent): void {
if (!this.context.draggable) {
return;
}
this.dragOverCounter = 0;
this.renderHeader();
this.context.draggable = null;
}
private onDrop(e: DragEvent): void {
if (!this.context.draggable) {
return;
}
this.dragOverCounter = 0;
this.renderHeader();
if (this.context.draggable !== this) {
this._onDidDrop.fire({ from: this.context.draggable.panel, to: this.panel });
}
this.context.draggable = null;
}
private renderHeader(): void {
this.panel.header.style.backgroundColor = this.dragOverCounter === 0 && this.dropBackground
? this.dropBackground.toString()
: null;
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
}
export class IPanelViewOptions {
dnd?: boolean;
}
@@ -166,19 +256,30 @@ interface IPanelItem {
export class PanelView implements IDisposable {
private dnd: boolean;
private dndContext: IDndContext = { draggable: null };
private el: HTMLElement;
private panelItems: IPanelItem[] = [];
private splitview: SplitView;
private animationTimer: number | null = null;
constructor(private container: HTMLElement, options?: IPanelViewOptions) {
this.dnd = !!options.dnd;
this.el = append(container, $('.monaco-panel-view'));
this.splitview = new SplitView(container);
}
addPanel(panel: Panel, size: number, index = this.splitview.length): void {
const disposable = panel.onDidChange(this.setupAnimation, this);
const panelItem = { panel, disposable };
const disposables: IDisposable[] = [];
panel.onDidChange(this.setupAnimation, this, disposables);
if (this.dnd) {
const draggable = new PanelDraggable(panel, this.dndContext);
disposables.push(draggable);
draggable.onDidDrop(({ from, to }) => this.movePanel(from, to), null, disposables);
}
const panelItem = { panel, disposable: combinedDisposable(disposables) };
this.panelItems.splice(index, 0, panelItem);
this.splitview.addView(panel, size, index);
@@ -196,6 +297,17 @@ export class PanelView implements IDisposable {
panelItem.disposable.dispose();
}
movePanel(from: Panel, to: Panel): void {
const fromIndex = firstIndex(this.panelItems, item => item.panel === from);
const toIndex = firstIndex(this.panelItems, item => item.panel === to);
if (fromIndex === -1 || toIndex === -1) {
return;
}
this.splitview.moveView(fromIndex, toIndex);
}
layout(size: number): void {
this.splitview.layout(size);
}