提交 a581627c 编写于 作者: B Benjamin Pasero 提交者: GitHub

Merge pull request #7811 from Microsoft/ben/dnd

Editor DND with split support
......@@ -932,8 +932,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
// Validate width ratios
const positions = rightEditors.length ? 3 : centerEditors.length ? 2 : 1;
if (!widthRatios || widthRatios.length !== positions) {
widthRatios = (positions === 3) ? [0.33, 0.33, 0.34] : (positions === 2) ? [0.5, 0.5] : [1];
if (widthRatios.length !== positions) {
widthRatios = void 0; // being taken care of by the layouting
}
// Open each input respecting the options. Since there can only be one active editor in each
......
......@@ -11,6 +11,14 @@
background-color: #252526;
}
#monaco-workbench-editor-move-overlay,
#monaco-workbench-editor-drop-overlay {
position: absolute;
left: 0;
width: 100%;
height: 100%;
z-index: 3000000;
}
.vs #monaco-workbench-editor-drop-overlay,
.vs .monaco-workbench .editor.empty > .content.dropfeedback {
......
......@@ -757,21 +757,52 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti
private enableDropTarget(node: HTMLElement): void {
const $this = this;
const overlayId = 'monaco-workbench-editor-drop-overlay';
const splitToPropertyKey = 'splitToPosition';
let overlay: Builder;
function onDrop(e: DragEvent, position: Position): void {
function onDrop(e: DragEvent, position: Position, splitTo?: Position): void {
DOM.removeClass(node, 'dropfeedback');
destroyOverlay();
const editorService = $this.editorService;
const groupService = $this.editorGroupService;
const stacks = groupService.getStacksModel();
const splitEditor = (typeof splitTo === 'number'); // TODO@Ben ugly split code should benefit from empty group support once available!
const freeGroup = (stacks.groups.length === 1) ? Position.CENTER : Position.RIGHT;
const pinned = EditorOptions.create({ pinned: true });
// Check for transfer from title control
const draggedEditor = TitleControl.getDraggedEditor();
if (draggedEditor) {
const isCopy = (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
// Copy editor to new location
if (isCopy) {
$this.editorService.openEditor(draggedEditor.editor, EditorOptions.create({ pinned: true }), position).done(null, errors.onUnexpectedError);
} else {
const sourcePosition = $this.stacks.positionOfGroup(draggedEditor.group);
$this.editorGroupService.moveEditor(draggedEditor.editor, sourcePosition, position);
if (splitEditor) {
editorService.openEditor(draggedEditor.editor, pinned, freeGroup).then(() => {
if (splitTo !== freeGroup) {
groupService.moveGroup(freeGroup, splitTo);
}
});
} else {
editorService.openEditor(draggedEditor.editor, pinned, position).done(null, errors.onUnexpectedError);
}
}
// Move editor to new location
else {
const sourcePosition = stacks.positionOfGroup(draggedEditor.group);
if (splitEditor) {
editorService.openEditor(draggedEditor.editor, pinned, freeGroup).then(() => {
if (splitTo !== freeGroup) {
groupService.moveGroup(freeGroup, splitTo);
}
groupService.moveEditor(draggedEditor.editor, stacks.positionOfGroup(draggedEditor.group), splitTo);
});
} else {
groupService.moveEditor(draggedEditor.editor, sourcePosition, position);
}
}
}
......@@ -782,8 +813,14 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti
window.focus(); // make sure this window has focus so that the open call reaches the right window!
// Open all
$this.editorService.openEditors(droppedResources.map(resource => { return { input: { resource, options: { pinned: true } }, position }; }))
.then(() => $this.editorGroupService.focusGroup(position))
editorService.openEditors(droppedResources.map(resource => { return { input: { resource, options: { pinned: true } }, position: splitEditor ? freeGroup : position }; }))
.then(() => {
if (splitEditor && splitTo !== freeGroup) {
groupService.moveGroup(freeGroup, splitTo);
}
groupService.focusGroup(splitEditor ? splitTo : position);
})
.done(null, errors.onUnexpectedError);
}
}
......@@ -796,25 +833,63 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti
}
}
function positionOverlay(e: DragEvent, groups: number, position: Position): void {
const target = <HTMLElement>e.target;
const posXOnOverlay = e.offsetX;
const overlayIsSplit = typeof overlay.getProperty(splitToPropertyKey) === 'number';
const overlayWidth = target.clientWidth;
const splitThreshold = overlayIsSplit ? overlayWidth / 5 : overlayWidth / 10;
const isCopy = (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
if (groups === POSITIONS.length) {
return; // do not show split feedback when we already at the maximum
}
const draggedEditor = TitleControl.getDraggedEditor();
if (!isCopy && draggedEditor && draggedEditor.group.count === 1) {
return; // do not show split feedback when moving the only one editor of a group
}
if (posXOnOverlay + splitThreshold > overlayWidth) {
overlay.setProperty(splitToPropertyKey, position === Position.LEFT ? Position.CENTER : Position.RIGHT);
overlay.style({
left: '50%',
width: '50%',
});
} else if (posXOnOverlay < splitThreshold) {
overlay.setProperty(splitToPropertyKey, position === Position.LEFT ? Position.LEFT : Position.CENTER);
overlay.style({
width: '50%'
});
} else {
overlay.removeProperty(splitToPropertyKey);
overlay.style({
left: '0',
width: '100%'
});
}
}
function createOverlay(target: HTMLElement): void {
if (!overlay) {
$this.visibleEditorContainers.forEach((container, index) => {
const containers = $this.visibleEditorContainers.filter(c => !!c);
containers.forEach((container, index) => {
if (container && DOM.isAncestor(target, container.getHTMLElement())) {
const useTabs = !!$this.configurationService.getConfiguration<IWorkbenchEditorConfiguration>().workbench.editor.showTabs;
overlay = $('div').style({
position: 'absolute',
top: useTabs ? SideBySideEditorControl.EDITOR_TITLE_HEIGHT + 'px' : 0,
left: 0,
width: '100%',
height: '100%',
zIndex: 3000000
top: useTabs ? SideBySideEditorControl.EDITOR_TITLE_HEIGHT + 'px' : 0
}).id(overlayId);
overlay.appendTo(container);
overlay.on(DOM.EventType.DROP, (e: DragEvent) => {
DOM.EventHelper.stop(e, true);
onDrop(e, index);
onDrop(e, index, overlay.getProperty(splitToPropertyKey));
});
overlay.on(DOM.EventType.DRAG_OVER, (e: DragEvent) => {
positionOverlay(e, containers.length, index);
});
overlay.on([DOM.EventType.DRAG_LEAVE, DOM.EventType.DRAG_END], () => {
......@@ -894,12 +969,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti
// Overlay the editor area with a div to be able to capture all mouse events
let overlayDiv = $('div').style({
position: 'absolute',
top: SideBySideEditorControl.EDITOR_TITLE_HEIGHT + 'px',
left: 0,
width: '100%',
height: '100%',
zIndex: 3000000
top: SideBySideEditorControl.EDITOR_TITLE_HEIGHT + 'px'
}).id('monaco-workbench-editor-move-overlay');
overlayDiv.appendTo(this.parent);
......
......@@ -241,7 +241,7 @@ export class Workbench implements IPartService {
return {
input: inputWithOptions.input,
options: inputWithOptions.options,
position: Math.min(index, Position.RIGHT) // put any resource > RIGHT to right position
position: Position.LEFT
};
});
......
......@@ -822,8 +822,11 @@ export class EditorStacksModel implements IEditorStacksModel {
this._groups.splice(index, 1);
this.groupToIdentifier[group.id] = void 0;
// Event
// Events
this.fireEvent(this._onGroupClosed, group, true);
for (let i = index; i < this._groups.length; i++) {
this.fireEvent(this._onGroupMoved, this._groups[i], true); // send move event for groups to the right that moved to the left into the closed group position
}
}
public closeGroups(except?: EditorGroup): void {
......@@ -867,7 +870,9 @@ export class EditorStacksModel implements IEditorStacksModel {
this._groups.splice(toIndex, 0, group);
// Event
this.fireEvent(this._onGroupMoved, group, true);
for (let i = Math.min(index, toIndex); i <= Math.max(index, toIndex) && i < this._groups.length; i++) {
this.fireEvent(this._onGroupMoved, this._groups[i], true); // send move event for groups to the right that moved to the left into the closed group position
}
}
private indexOf(group: EditorGroup): number {
......
......@@ -99,6 +99,8 @@ export class UntitledEditorInput extends AbstractUntitledEditorInput {
public revert(): TPromise<boolean> {
this.cachedModel.revert();
this.dispose(); // a reverted untitled editor is no longer valid, so we dispose it
return TPromise.as(true);
}
......
......@@ -85,9 +85,14 @@ export class FileTracker implements IWorkbenchContribution {
}
// If a file becomes dirty but is not opened, we open it in the background
if (!this.stacks.isOpen(e.resource)) {
this.editorService.openEditor({ resource: e.resource, options: { inactive: true } }).done(null, errors.onUnexpectedError);
}
// Since it might be the intent of whoever created the model to show it shortly
// after, we delay this a little bit and check again if the editor has not been
// opened meanwhile
setTimeout(() => {
if (!this.stacks.isOpen(e.resource) && this.textFileService.isDirty(e.resource)) {
this.editorService.openEditor({ resource: e.resource, options: { inactive: true } }).done(null, errors.onUnexpectedError);
}
}, 500);
}
private onTextFileSaveError(e: TextFileChangeEvent): void {
......
......@@ -23,6 +23,7 @@ import {FileImportedEvent, RefreshViewExplorerAction, NewFolderAction, NewFileAc
import {FileEditorInput} from 'vs/workbench/parts/files/browser/editors/fileEditorInput';
import {FileDragAndDrop, FileFilter, FileSorter, FileController, FileRenderer, FileDataSource, FileViewletState, FileAccessibilityProvider} from 'vs/workbench/parts/files/browser/views/explorerViewer';
import lifecycle = require('vs/base/common/lifecycle');
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
import {IEditor} from 'vs/platform/editor/common/editor';
import {IEditorGroupService} from 'vs/workbench/services/group/common/groupService';
import * as DOM from 'vs/base/browser/dom';
......@@ -172,8 +173,8 @@ export class ExplorerView extends CollapsibleViewletView {
}
}
// Handle closed (convince explorer to not reopen any file when getting visible)
if (!activeInput) {
// Handle closed or untitled file (convince explorer to not reopen any file when getting visible)
if (activeInput instanceof UntitledEditorInput || !activeInput) {
this.settings[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = void 0;
clearFocus = true;
}
......
......@@ -137,10 +137,11 @@ export class FileTracker implements IWorkbenchContribution {
}
// Otherwise open all
const activeEditor = this.editorService.getActiveEditor();
return this.editorService.openEditors(resources.map((r, index) => {
return {
input: r,
position: Math.min(index, Position.RIGHT) // put any resource > RIGHT to right position
position: activeEditor ? activeEditor.position : Position.LEFT
};
}));
});
......
......@@ -266,6 +266,21 @@ suite('Editor Stacks Model', () => {
assert.equal(model.groups.length, 0);
});
test('Groups - Close Group sends move event for others to the right', function () {
const model = create();
const events = modelListener(model);
const first = model.openGroup('first');
model.openGroup('second');
const third = model.openGroup('third');
model.closeGroup(first);
assert.equal(events.moved.length, 2);
model.closeGroup(third);
assert.equal(events.moved.length, 2);
});
test('Groups - Move Groups', function () {
const model = create();
const events = modelListener(model);
......@@ -293,13 +308,11 @@ suite('Editor Stacks Model', () => {
test('Groups - Rename Group', function () {
const model = create();
const events = modelListener(model);
const group1 = model.openGroup('first');
const group2 = model.openGroup('second');
model.moveGroup(group1, 1);
assert.equal(events.moved[0], group1);
assert.equal(model.groups[0], group2);
assert.equal(model.groups[1], group1);
......@@ -316,6 +329,38 @@ suite('Editor Stacks Model', () => {
assert.equal(model.groups[2], group1);
});
test('Groups - Move Group sends move events for all moved groups', function () {
const model = create();
let events = modelListener(model);
let group1 = model.openGroup('first');
let group2 = model.openGroup('second');
let group3 = model.openGroup('third');
model.moveGroup(group1, 1);
assert.equal(events.moved.length, 2);
model.closeGroups();
events = modelListener(model);
group1 = model.openGroup('first');
group2 = model.openGroup('second');
group3 = model.openGroup('third');
model.moveGroup(group1, 2);
assert.equal(events.moved.length, 3);
model.closeGroups();
events = modelListener(model);
group1 = model.openGroup('first');
group2 = model.openGroup('second');
group3 = model.openGroup('third');
model.moveGroup(group3, 1);
assert.equal(events.moved.length, 2);
});
test('Groups - Event Aggregation', function () {
const model = create();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册