提交 708a5d41 编写于 作者: B Benjamin Pasero

grid - more complete DND implementation

上级 3760a4bd
......@@ -22,6 +22,17 @@ export interface IEditorService {
openEditor(input: IResourceInput, sideBySide?: boolean): TPromise<IEditor>;
}
export interface IEditorInputWithOptions {
editor: IEditorInput;
options?: IEditorOptions;
}
export function isEditorInputWithOptions(obj: any): obj is IEditorInputWithOptions {
const editorInputWithOptions = obj as IEditorInputWithOptions;
return !!editorInputWithOptions && !!editorInputWithOptions.editor;
}
export interface IEditorModel {
onDispose: Event<void>;
......
......@@ -12,12 +12,10 @@ import { IWindowsService, IWindowService } from 'vs/platform/windows/common/wind
import URI from 'vs/base/common/uri';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { TPromise } from 'vs/base/common/winjs.base';
import { Schemas } from 'vs/base/common/network';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Position } from 'vs/platform/editor/common/editor';
import { onUnexpectedError } from 'vs/base/common/errors';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
......@@ -32,8 +30,9 @@ import { isWindows } from 'vs/base/common/platform';
import { coalesce } from 'vs/base/common/arrays';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { IEditorIdentifier } from 'vs/workbench/common/editor';
import { IEditorIdentifier, GroupIdentifier } from 'vs/workbench/common/editor';
import { basenameOrAuthority } from 'vs/base/common/resources';
import { INextEditorService, IResourceEditor } from 'vs/workbench/services/editor/common/nextEditorService';
export interface IDraggedResource {
resource: URI;
......@@ -41,10 +40,9 @@ export interface IDraggedResource {
}
export class DraggedEditorIdentifier {
constructor(private _identifier: IEditorIdentifier) {
}
constructor(private _identifier: IEditorIdentifier) { }
public get identifier(): IEditorIdentifier {
get identifier(): IEditorIdentifier {
return this._identifier;
}
}
......@@ -155,14 +153,13 @@ export class ResourcesDropHandler {
@IWorkspacesService private workspacesService: IWorkspacesService,
@ITextFileService private textFileService: ITextFileService,
@IBackupFileService private backupFileService: IBackupFileService,
@IEditorGroupService private groupService: IEditorGroupService,
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@INextEditorService private editorService: INextEditorService,
@IConfigurationService private configurationService: IConfigurationService
) {
}
public handleDrop(event: DragEvent, afterDrop: () => void, targetPosition: Position, targetIndex?: number): void {
handleDrop(event: DragEvent, afterDrop: (targetGroup: GroupIdentifier) => void, resolveTargetGroup: () => GroupIdentifier, targetIndex?: number): void {
const untitledOrFileResources = extractResources(event).filter(r => this.fileService.canHandleResource(r.resource) || r.resource.scheme === Schemas.untitled);
if (!untitledOrFileResources.length) {
return;
......@@ -183,23 +180,21 @@ export class ResourcesDropHandler {
this.windowsService.addRecentlyOpened(filesToAddToHistory.map(resource => resource.fsPath));
}
const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({
resource: untitledOrFileResource.resource,
options: {
pinned: true,
index: targetIndex,
viewState: (untitledOrFileResource as IDraggedEditor).viewState
}
}));
// Open in Editor
return this.editorService.openEditors(untitledOrFileResources.map(untitledOrFileResource => {
return {
input: {
resource: untitledOrFileResource.resource,
options: {
pinned: true,
index: targetIndex,
viewState: (untitledOrFileResource as IDraggedEditor).viewState
}
},
position: targetPosition
};
})).then(() => {
const targetGroup = resolveTargetGroup();
return this.editorService.openEditors(editors, targetGroup).then(() => {
// Finish with provided function
afterDrop();
afterDrop(targetGroup);
});
});
}).done(null, onUnexpectedError);
......@@ -232,7 +227,7 @@ export class ResourcesDropHandler {
}
// Return early if the resource is already dirty in target or opened already
if (this.textFileService.isDirty(droppedDirtyEditor.resource) || this.groupService.getStacksModel().isOpen(droppedDirtyEditor.resource)) {
if (this.textFileService.isDirty(droppedDirtyEditor.resource) || this.editorService.isOpen({ resource: droppedDirtyEditor.resource })) {
return TPromise.as(false);
}
......@@ -316,7 +311,7 @@ export class SimpleFileResourceDragAndDrop extends DefaultDragAndDrop {
super();
}
public getDragURI(tree: ITree, obj: any): string {
getDragURI(tree: ITree, obj: any): string {
const resource = this.toResource(obj);
if (resource) {
return resource.toString();
......@@ -325,7 +320,7 @@ export class SimpleFileResourceDragAndDrop extends DefaultDragAndDrop {
return void 0;
}
public getDragLabel(tree: ITree, elements: any[]): string {
getDragLabel(tree: ITree, elements: any[]): string {
if (elements.length > 1) {
return String(elements.length);
}
......@@ -338,7 +333,7 @@ export class SimpleFileResourceDragAndDrop extends DefaultDragAndDrop {
return void 0;
}
public onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void {
onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void {
// Apply some datatransfer types to allow for dragging the element outside of the application
const resources: URI[] = data.getData().map(source => this.toResource(source));
......@@ -428,20 +423,20 @@ export class LocalSelectionTransfer<T> {
// protect against external instantiation
}
public static getInstance<T>(): LocalSelectionTransfer<T> {
static getInstance<T>(): LocalSelectionTransfer<T> {
return LocalSelectionTransfer.INSTANCE as LocalSelectionTransfer<T>;
}
public hasData(proto: T): boolean {
hasData(proto: T): boolean {
return proto && proto === this.proto;
}
public clearData(): void {
clearData(): void {
this.proto = void 0;
this.data = void 0;
}
public getData(proto: T): T[] {
getData(proto: T): T[] {
if (this.hasData(proto)) {
return this.data;
}
......@@ -449,7 +444,7 @@ export class LocalSelectionTransfer<T> {
return void 0;
}
public setData(data: T[], proto: T): void {
setData(data: T[], proto: T): void {
if (proto) {
this.data = data;
this.proto = proto;
......
......@@ -137,7 +137,7 @@ export class BaseSplitEditorGroupAction extends Action {
}
// Add group
const newGroup = this.nextEditorGroupsService.addGroup(group, this.direction);
const newGroup = this.nextEditorGroupsService.addGroup(group, this.direction, { activate: true });
// Open editor
if (activeEditor) {
......
......@@ -1209,7 +1209,7 @@ export class EditorGroupsControl extends Themable implements IEditorGroupsContro
}
groupService.focusGroup(splitEditor ? splitTo : position);
}, splitEditor ? freeGroup : position);
}, () => splitEditor ? freeGroup : position);
}
}
......
......@@ -404,7 +404,120 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
//#endregion
//#region TODO@grid openEditors()
//#region Handled or Adopted or Obsolete
public _serviceBrand: any;
private static readonly GROUP_LEFT = nls.localize('groupOneVertical', "Left");
private static readonly GROUP_CENTER = nls.localize('groupTwoVertical', "Center");
private static readonly GROUP_RIGHT = nls.localize('groupThreeVertical', "Right");
private static readonly GROUP_TOP = nls.localize('groupOneHorizontal', "Top");
private static readonly GROUP_MIDDLE = nls.localize('groupTwoHorizontal', "Center");
private static readonly GROUP_BOTTOM = nls.localize('groupThreeHorizontal', "Bottom");
private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.uiState';
private dimension: Dimension;
private editorGroupsControl: IEditorGroupsControl;
private memento: object;
private stacks: EditorStacksModel;
private tabOptions: IEditorTabOptions;
private forceHideTabs: boolean;
private revealIfOpen: boolean;
private ignoreOpenEditorErrors: boolean;
private textCompareEditorVisible: IContextKey<boolean>;
private readonly _onEditorsChanged: ThrottledEmitter<void>;
private readonly _onEditorOpening: Emitter<IEditorOpeningEvent>;
private readonly _onEditorGroupMoved: Emitter<void>;
private readonly _onEditorOpenFail: Emitter<EditorInput>;
private readonly _onGroupOrientationChanged: Emitter<void>;
private readonly _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private readonly _onLayout: Emitter<Dimension>;
// The following data structures are partitioned into array of Position as provided by Services.POSITION array
private visibleEditors: BaseEditor[];
private instantiatedEditors: BaseEditor[][];
private editorOpenToken: number[];
private pendingEditorInputsToClose: EditorIdentifier[];
private pendingEditorInputCloseTimeout: number;
public get onEditorGroupMoved(): Event<void> {
return this._onEditorGroupMoved.event;
}
public get onEditorsChanged(): Event<void> {
return this._onEditorsChanged.event;
}
constructor(
id: string,
restoreFromStorage: boolean,
@INotificationService private notificationService: INotificationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IStorageService private storageService: IStorageService,
@IPartService private partService: IPartService,
@IConfigurationService private configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
super(id, { hasTitle: false }, themeService);
this._onEditorsChanged = new ThrottledEmitter<void>();
this._onEditorOpening = new Emitter<IEditorOpeningEvent>();
this._onEditorGroupMoved = new Emitter<void>();
this._onEditorOpenFail = new Emitter<EditorInput>();
this._onGroupOrientationChanged = new Emitter<void>();
this._onTabOptionsChanged = new Emitter<IEditorTabOptions>();
this._onLayout = new Emitter<Dimension>();
this.visibleEditors = [];
this.editorOpenToken = arrays.fill(POSITIONS.length, () => 0);
this.instantiatedEditors = arrays.fill(POSITIONS.length, () => []);
this.pendingEditorInputsToClose = [];
this.pendingEditorInputCloseTimeout = null;
this.stacks = this.instantiationService.createInstance(EditorStacksModel, restoreFromStorage);
this.textCompareEditorVisible = TextCompareEditorVisibleContext.bindTo(contextKeyService);
const config = configurationService.getValue<IWorkbenchEditorConfiguration>();
if (config && config.workbench && config.workbench.editor) {
const editorConfig = config.workbench.editor;
this.tabOptions = {
previewEditors: editorConfig.enablePreview,
showIcons: editorConfig.showIcons,
showTabs: editorConfig.showTabs,
tabCloseButton: editorConfig.tabCloseButton,
tabSizing: editorConfig.tabSizing,
labelFormat: editorConfig.labelFormat,
iconTheme: config.workbench.iconTheme
};
this.revealIfOpen = editorConfig.revealIfOpen;
} else {
this.tabOptions = {
previewEditors: true,
showIcons: false,
showTabs: true,
tabCloseButton: 'right',
tabSizing: 'fit',
labelFormat: 'default',
iconTheme: 'vs-seti'
};
this.revealIfOpen = false;
}
this.initStyles();
this.registerListeners();
}
public openEditors(editors: { input: EditorInput, position?: Position, options?: EditorOptions }[]): TPromise<IEditor[]>;
public openEditors(editors: { input: EditorInput, options?: EditorOptions }[], sideBySide?: boolean): TPromise<IEditor[]>;
......@@ -528,123 +641,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
});
}
//#endregion
//#region Handled or Adopted or Obsolete
public _serviceBrand: any;
private static readonly GROUP_LEFT = nls.localize('groupOneVertical', "Left");
private static readonly GROUP_CENTER = nls.localize('groupTwoVertical', "Center");
private static readonly GROUP_RIGHT = nls.localize('groupThreeVertical', "Right");
private static readonly GROUP_TOP = nls.localize('groupOneHorizontal', "Top");
private static readonly GROUP_MIDDLE = nls.localize('groupTwoHorizontal', "Center");
private static readonly GROUP_BOTTOM = nls.localize('groupThreeHorizontal', "Bottom");
private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.uiState';
private dimension: Dimension;
private editorGroupsControl: IEditorGroupsControl;
private memento: object;
private stacks: EditorStacksModel;
private tabOptions: IEditorTabOptions;
private forceHideTabs: boolean;
private revealIfOpen: boolean;
private ignoreOpenEditorErrors: boolean;
private textCompareEditorVisible: IContextKey<boolean>;
private readonly _onEditorsChanged: ThrottledEmitter<void>;
private readonly _onEditorOpening: Emitter<IEditorOpeningEvent>;
private readonly _onEditorGroupMoved: Emitter<void>;
private readonly _onEditorOpenFail: Emitter<EditorInput>;
private readonly _onGroupOrientationChanged: Emitter<void>;
private readonly _onTabOptionsChanged: Emitter<IEditorTabOptions>;
private readonly _onLayout: Emitter<Dimension>;
// The following data structures are partitioned into array of Position as provided by Services.POSITION array
private visibleEditors: BaseEditor[];
private instantiatedEditors: BaseEditor[][];
private editorOpenToken: number[];
private pendingEditorInputsToClose: EditorIdentifier[];
private pendingEditorInputCloseTimeout: number;
public get onEditorGroupMoved(): Event<void> {
return this._onEditorGroupMoved.event;
}
public get onEditorsChanged(): Event<void> {
return this._onEditorsChanged.event;
}
constructor(
id: string,
restoreFromStorage: boolean,
@INotificationService private notificationService: INotificationService,
@ITelemetryService private telemetryService: ITelemetryService,
@IStorageService private storageService: IStorageService,
@IPartService private partService: IPartService,
@IConfigurationService private configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
super(id, { hasTitle: false }, themeService);
this._onEditorsChanged = new ThrottledEmitter<void>();
this._onEditorOpening = new Emitter<IEditorOpeningEvent>();
this._onEditorGroupMoved = new Emitter<void>();
this._onEditorOpenFail = new Emitter<EditorInput>();
this._onGroupOrientationChanged = new Emitter<void>();
this._onTabOptionsChanged = new Emitter<IEditorTabOptions>();
this._onLayout = new Emitter<Dimension>();
this.visibleEditors = [];
this.editorOpenToken = arrays.fill(POSITIONS.length, () => 0);
this.instantiatedEditors = arrays.fill(POSITIONS.length, () => []);
this.pendingEditorInputsToClose = [];
this.pendingEditorInputCloseTimeout = null;
this.stacks = this.instantiationService.createInstance(EditorStacksModel, restoreFromStorage);
this.textCompareEditorVisible = TextCompareEditorVisibleContext.bindTo(contextKeyService);
const config = configurationService.getValue<IWorkbenchEditorConfiguration>();
if (config && config.workbench && config.workbench.editor) {
const editorConfig = config.workbench.editor;
this.tabOptions = {
previewEditors: editorConfig.enablePreview,
showIcons: editorConfig.showIcons,
showTabs: editorConfig.showTabs,
tabCloseButton: editorConfig.tabCloseButton,
tabSizing: editorConfig.tabSizing,
labelFormat: editorConfig.labelFormat,
iconTheme: config.workbench.iconTheme
};
this.revealIfOpen = editorConfig.revealIfOpen;
} else {
this.tabOptions = {
previewEditors: true,
showIcons: false,
showTabs: true,
tabCloseButton: 'right',
tabSizing: 'fit',
labelFormat: 'default',
iconTheme: 'vs-seti'
};
this.revealIfOpen = false;
}
this.initStyles();
this.registerListeners();
}
public moveGroup(from: EditorGroup, to: EditorGroup): void;
public moveGroup(from: Position, to: Position): void;
public moveGroup(arg1: any, arg2: any): void {
......
......@@ -7,7 +7,7 @@
import { GroupIdentifier, IWorkbenchEditorConfiguration, IWorkbenchEditorPartConfiguration, EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor';
import { EditorGroup } from 'vs/workbench/common/editor/editorStacksModel';
import { INextEditorGroup, GroupDirection } from 'vs/workbench/services/group/common/nextEditorGroupsService';
import { INextEditorGroup, GroupDirection, IAddGroupOptions } from 'vs/workbench/services/group/common/nextEditorGroupsService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Dimension } from 'vs/base/browser/dom';
import { Event } from 'vs/base/common/event';
......@@ -73,7 +73,7 @@ export interface INextEditorGroupsAccessor {
getGroup(identifier: GroupIdentifier): INextEditorGroupView;
addGroup(location: INextEditorGroupView | GroupIdentifier, direction: GroupDirection, copyGroup?: boolean): INextEditorGroup;
addGroup(location: INextEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): INextEditorGroup;
}
export interface INextEditorGroupView extends IDisposable, ISerializableView, INextEditorGroup {
......
......@@ -6,7 +6,7 @@
'use strict';
import 'vs/css!./media/nextEditorDragAndDrop';
import { LocalSelectionTransfer, DraggedEditorIdentifier, DragCounter } from 'vs/workbench/browser/dnd';
import { LocalSelectionTransfer, DraggedEditorIdentifier, DragCounter, ResourcesDropHandler } from 'vs/workbench/browser/dnd';
import { addDisposableListener, EventType, EventHelper, isAncestor, toggleClass, addClass } from 'vs/base/browser/dom';
import { INextEditorGroupsAccessor, EDITOR_TITLE_HEIGHT, INextEditorGroupView, getActiveTextEditorOptions } from 'vs/workbench/browser/parts/editor2/editor2';
import { EDITOR_DRAG_AND_DROP_BACKGROUND, Themable } from 'vs/workbench/common/theme';
......@@ -16,6 +16,7 @@ import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/comm
import { isMacintosh } from 'vs/base/common/platform';
import { GroupDirection, INextEditorGroup } from 'vs/workbench/services/group/common/nextEditorGroupsService';
import { toDisposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
class DropOverlay extends Themable {
......@@ -32,7 +33,8 @@ class DropOverlay extends Themable {
private accessor: INextEditorGroupsAccessor,
private groupView: INextEditorGroupView,
private transfer: LocalSelectionTransfer<DraggedEditorIdentifier>,
themeService: IThemeService
themeService: IThemeService,
private instantiationService: IInstantiationService
) {
super(themeService);
......@@ -123,34 +125,47 @@ class DropOverlay extends Themable {
}
private handleDrop(event: DragEvent): void {
if (!this.transfer.hasData(DraggedEditorIdentifier.prototype)) {
return; // TODO@grid support more drops
}
const draggedEditor = this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
// Determine target group
let targetGroup: INextEditorGroup;
if (typeof this.splitDirection === 'number') {
targetGroup = this.accessor.addGroup(this.groupView, this.splitDirection);
} else {
targetGroup = this.groupView;
}
const ensureTargetGroup = () => {
let targetGroup: INextEditorGroup;
if (typeof this.splitDirection === 'number') {
targetGroup = this.accessor.addGroup(this.groupView, this.splitDirection, { activate: true });
} else {
targetGroup = this.groupView;
}
// Return if the drop is a no-op
const sourceGroup = this.accessor.getGroup(draggedEditor.group.id);
if (sourceGroup === targetGroup) {
return;
}
return targetGroup;
};
// Check for transfer from title control
if (this.transfer.hasData(DraggedEditorIdentifier.prototype)) {
const draggedEditor = this.transfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const targetGroup = ensureTargetGroup();
// Open in target group
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
targetGroup.openEditor(draggedEditor.editor, options);
// Return if the drop is a no-op
const sourceGroup = this.accessor.getGroup(draggedEditor.group.id);
if (sourceGroup === targetGroup) {
return;
}
// Open in target group
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
targetGroup.openEditor(draggedEditor.editor, options);
// Close in source group unless we copy
const copyEditor = this.shouldCopyEditor(draggedEditor, event);
if (!copyEditor) {
sourceGroup.closeEditor(draggedEditor.editor);
}
}
// Close in source group unless we copy
const copyEditor = this.shouldCopyEditor(draggedEditor, event);
if (!copyEditor) {
sourceGroup.closeEditor(draggedEditor.editor);
// Check for URI transfer
else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
dropHandler.handleDrop(event, targetGroupIdentifier => {
this.accessor.getGroup(targetGroupIdentifier).focus();
}, () => ensureTargetGroup().id);
}
}
......@@ -272,7 +287,8 @@ export class NextEditorDragAndDrop extends Themable {
constructor(
private accessor: INextEditorGroupsAccessor,
private container: HTMLElement,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@IInstantiationService private instantiationService: IInstantiationService
) {
super(themeService);
......@@ -314,7 +330,7 @@ export class NextEditorDragAndDrop extends Themable {
if (!this.overlay) {
const groupView = this.findGroupView(target);
if (groupView) {
this._overlay = new DropOverlay(this.accessor, groupView, this.transfer, this.themeService);
this._overlay = new DropOverlay(this.accessor, groupView, this.transfer, this.themeService, this.instantiationService);
}
}
......
......@@ -628,6 +628,32 @@ export class NextEditorGroupView extends Themable implements INextEditorGroupVie
//#endregion
//#region openEditors()
openEditors(editors: { editor: EditorInput, options?: EditorOptions }[]): Thenable<void> {
if (!editors.length) {
return TPromise.as(void 0);
}
// Use the first editor as active editor
const { editor, options } = editors.shift();
return this.openEditor(editor, options).then(() => {
const startingIndex = this.getIndexOfEditor(editor) + 1;
// Open the other ones inactive
return TPromise.join(editors.map(({ editor, options }, index) => {
const adjustedEditorOptions = options || new EditorOptions();
adjustedEditorOptions.inactive = true;
adjustedEditorOptions.pinned = true;
adjustedEditorOptions.index = startingIndex + index;
return this.openEditor(editor, adjustedEditorOptions);
})).then(() => void 0);
});
}
//#endregion
//#region moveEditor()
moveEditor(editor: EditorInput, target: INextEditorGroupView, options?: IMoveEditorOptions): void {
......
......@@ -11,7 +11,7 @@ import { Part } from 'vs/workbench/browser/part';
import { Dimension, isAncestor, toggleClass, addClass, clearNode } from 'vs/base/browser/dom';
import { Event, Emitter, once } from 'vs/base/common/event';
import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { INextEditorGroupsService, GroupDirection } from 'vs/workbench/services/group/common/nextEditorGroupsService';
import { INextEditorGroupsService, GroupDirection, IAddGroupOptions } from 'vs/workbench/services/group/common/nextEditorGroupsService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, ISerializedNode } from 'vs/base/browser/ui/grid/grid';
import { GroupIdentifier, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
......@@ -34,10 +34,6 @@ import { IWindowService } from 'vs/platform/windows/common/windows';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { NextEditorDragAndDrop } from './nextEditorDragAndDrop';
// TODO@grid provide DND support of groups/editors:
// - editor: move/copy to existing group, move/copy to new split group (up, down, left, right)
// - group: move/copy to existing group (merges?), move/copy to new split group (up, down, left, right)
// TODO@grid enable minimized/maximized groups in one dimension
interface INextEditorPartUIState {
......@@ -198,10 +194,16 @@ export class NextEditorPart extends Part implements INextEditorGroupsService, IN
return groupView;
}
addGroup(location: INextEditorGroupView | GroupIdentifier, direction: GroupDirection, copy?: boolean): INextEditorGroupView {
addGroup(location: INextEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): INextEditorGroupView {
const locationView = this.assertGroupView(location);
return this.doAddGroup(locationView, direction, copy ? locationView : void 0);
const group = this.doAddGroup(locationView, direction, options && options.copyGroup ? locationView : void 0);
if (options && options.activate) {
this.doSetGroupActive(group);
}
return group;
}
private doAddGroup(locationView: INextEditorGroupView, direction: GroupDirection, groupToCopy?: INextEditorGroupView): INextEditorGroupView {
......
......@@ -32,7 +32,7 @@ import { getOrSet } from 'vs/base/common/map';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_BACKGROUND, WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
import { activeContrastBorder, contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { ResourcesDropHandler, fillResourceDataTransfers, LocalSelectionTransfer, DraggedEditorIdentifier } from 'vs/workbench/browser/dnd';
import { ResourcesDropHandler, fillResourceDataTransfers, LocalSelectionTransfer, DraggedEditorIdentifier, DragCounter } from 'vs/workbench/browser/dnd';
import { Color } from 'vs/base/common/color';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
......@@ -508,11 +508,11 @@ export class NextTabsTitleControl extends NextTitleControl {
// it contains a label and a close button. HTML gives us DRAG_ENTER and DRAG_LEAVE events when hovering over
// these children and this can cause flicker of the drop feedback. The workaround is to count the events and only
// remove the drop feedback when the counter is 0 (see https://github.com/Microsoft/vscode/issues/14470)
let counter = 0;
const counter = new DragCounter();
// Drag over
disposables.push(addDisposableListener(tab, EventType.DRAG_ENTER, (e: DragEvent) => {
counter++;
counter.increment();
// Find out if the currently dragged editor is this tab and in that
// case we do not want to show any drop feedback
......@@ -533,8 +533,9 @@ export class NextTabsTitleControl extends NextTitleControl {
// Drag leave
disposables.push(addDisposableListener(tab, EventType.DRAG_LEAVE, (e: DragEvent) => {
counter--;
if (counter === 0) {
counter.decrement();
if (!counter.value) {
removeClass(tab, 'dragged-over');
this.updateDropFeedback(tab, false, index);
}
......@@ -542,7 +543,8 @@ export class NextTabsTitleControl extends NextTitleControl {
// Drag end
disposables.push(addDisposableListener(tab, EventType.DRAG_END, (e: DragEvent) => {
counter = 0;
counter.reset();
removeClass(tab, 'dragged-over');
this.updateDropFeedback(tab, false, index);
......@@ -551,7 +553,8 @@ export class NextTabsTitleControl extends NextTitleControl {
// Drop
disposables.push(addDisposableListener(tab, EventType.DROP, (e: DragEvent) => {
counter = 0;
counter.reset();
removeClass(tab, 'dragged-over');
this.updateDropFeedback(tab, false, index);
......@@ -929,7 +932,7 @@ export class NextTabsTitleControl extends NextTitleControl {
// External DND
else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false /* open workspace file as file if dropped */ });
dropHandler.handleDrop(e, () => this.group.focus(), this.group.id /* TODO@grid position => group id */, targetIndex);
dropHandler.handleDrop(e, () => this.group.focus(), () => this.group.id, targetIndex);
}
}
......
......@@ -497,7 +497,7 @@ class EditorGroupRenderer implements IRenderer<IEditorGroup, IEditorGroupTemplat
this.editorGroupService.activateGroup(positionOfTargetGroup);
} else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false });
dropHandler.handleDrop(e, () => this.editorGroupService.activateGroup(positionOfTargetGroup), positionOfTargetGroup);
dropHandler.handleDrop(e, () => this.editorGroupService.activateGroup(positionOfTargetGroup), () => positionOfTargetGroup);
}
}));
......@@ -584,7 +584,7 @@ class OpenEditorRenderer implements IRenderer<OpenEditor, IOpenEditorTemplateDat
this.editorGroupService.activateGroup(positionOfTargetGroup);
} else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false });
dropHandler.handleDrop(e, () => this.editorGroupService.activateGroup(positionOfTargetGroup), positionOfTargetGroup, index);
dropHandler.handleDrop(e, () => this.editorGroupService.activateGroup(positionOfTargetGroup), () => positionOfTargetGroup, index);
}
}));
editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_END, () => {
......
......@@ -6,7 +6,7 @@
'use strict';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IEditorInput, IResourceInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditor, ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorInput, IResourceInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditor, ITextEditorOptions, IEditorOptions, IEditorInputWithOptions, isEditorInputWithOptions } from 'vs/platform/editor/common/editor';
import { GroupIdentifier, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, EditorOptions, TextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/common/editor';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { DataUriEditorInput } from 'vs/workbench/common/editor/dataUriEditorInput';
......@@ -25,7 +25,7 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { INextEditorGroupsService, INextEditorGroup, GroupDirection } from 'vs/workbench/services/group/common/nextEditorGroupsService';
import { INextEditorService, IResourceEditor, SIDE_BY_SIDE_TYPE, SIDE_BY_SIDE, IEditorInputWithOptions } from 'vs/workbench/services/editor/common/nextEditorService';
import { INextEditorService, IResourceEditor, SIDE_BY_SIDE_TYPE, SIDE_BY_SIDE } from 'vs/workbench/services/editor/common/nextEditorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { coalesce } from 'vs/base/common/arrays';
......@@ -173,7 +173,10 @@ export class NextEditorService extends Disposable implements INextEditorService
// Typed Editor Support
if (editor instanceof EditorInput) {
return this.doOpenEditor(editor, this.toOptions(optionsOrGroup as IEditorOptions), group);
const editorOptions = this.toOptions(optionsOrGroup as IEditorOptions);
const targetGroup = this.findTargetGroup(editor, editorOptions, group);
return targetGroup.openEditor(editor, editorOptions).then(() => targetGroup.activeControl);
}
// Throw error for well known foreign resources (such as a http link) (TODO@ben remove me after this has been adopted)
......@@ -189,18 +192,21 @@ export class NextEditorService extends Disposable implements INextEditorService
const textInput = <IResourceEditor>editor;
const typedInput = this.createInput(textInput);
if (typedInput) {
return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), optionsOrGroup as GroupIdentifier);
const editorOptions = TextEditorOptions.from(textInput);
const targetGroup = this.findTargetGroup(typedInput, editorOptions, optionsOrGroup as GroupIdentifier);
return targetGroup.openEditor(typedInput, editorOptions).then(() => targetGroup.activeControl);
}
return TPromise.wrap<IEditor>(null);
}
private doOpenEditor(input: IEditorInput, options?: EditorOptions, group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor> {
private findTargetGroup(input: IEditorInput, options?: IEditorOptions, group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): INextEditorGroup {
let targetGroup: INextEditorGroup;
// Group: Side by Side
if (group === SIDE_BY_SIDE) {
targetGroup = this.nextEditorGroupsService.addGroup(this.nextEditorGroupsService.activeGroup, GroupDirection.RIGHT);
targetGroup = this.createSideBySideGroup();
}
// Group: Specific Group
......@@ -240,7 +246,11 @@ export class NextEditorService extends Disposable implements INextEditorService
targetGroup = this.nextEditorGroupsService.activeGroup;
}
return targetGroup.openEditor(input, options).then(() => targetGroup.activeControl);
return targetGroup;
}
private createSideBySideGroup(): INextEditorGroup {
return this.nextEditorGroupsService.addGroup(this.nextEditorGroupsService.activeGroup, GroupDirection.RIGHT);
}
private toOptions(options?: IEditorOptions | EditorOptions): EditorOptions {
......@@ -260,13 +270,45 @@ export class NextEditorService extends Disposable implements INextEditorService
//#region openEditors()
// TODO@grid openEditors()
openEditors(editors: IEditorInputWithOptions[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor>;
openEditors(editors: IResourceEditor[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor>;
openEditors(editors: (IEditorInputWithOptions | IResourceEditor)[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor> {
const inputs = editors.map(editor => this.createInput(editor));
openEditors(editors: IEditorInputWithOptions[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor[]>;
openEditors(editors: IResourceEditor[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor[]>;
openEditors(editors: (IEditorInputWithOptions | IResourceEditor)[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor[]> {
// Convert to typed editors and options
const typedEditors: IEditorInputWithOptions[] = [];
editors.forEach(editor => {
if (isEditorInputWithOptions(editor)) {
typedEditors.push(editor);
} else {
typedEditors.push({ editor: this.createInput(editor), options: TextEditorOptions.from(editor) });
}
});
// Find target groups to open
const mapGroupToEditors = new Map<INextEditorGroup, IEditorInputWithOptions[]>();
if (group === SIDE_BY_SIDE) {
mapGroupToEditors.set(this.createSideBySideGroup(), typedEditors);
} else {
typedEditors.forEach(typedEditor => {
const targetGroup = this.findTargetGroup(typedEditor.editor, typedEditor.options, group);
let targetGroupEditors = mapGroupToEditors.get(targetGroup);
if (!targetGroupEditors) {
targetGroupEditors = [];
mapGroupToEditors.set(targetGroup, targetGroupEditors);
}
targetGroupEditors.push(typedEditor);
});
}
// Open in targets
const result: TPromise<IEditor>[] = [];
mapGroupToEditors.forEach((editorsWithOptions, group) => {
result.push((group.openEditors(editorsWithOptions) as TPromise).then(() => group.activeControl));
});
return this.openEditor(inputs[0]);
return TPromise.join(result);
}
//#endregion
......
......@@ -6,7 +6,7 @@
'use strict';
import { createDecorator, ServiceIdentifier, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IEditorInput, IResourceInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditor, IEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorInput, IResourceInput, IUntitledResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditor, IEditorOptions, IEditorInputWithOptions } from 'vs/platform/editor/common/editor';
import { GroupIdentifier, IEditorOpeningEvent } from 'vs/workbench/common/editor';
import { Event } from 'vs/base/common/event';
import { IEditor as ICodeEditor } from 'vs/editor/common/editorCommon';
......@@ -18,11 +18,6 @@ export type IResourceEditor = IResourceInput | IUntitledResourceInput | IResourc
export const SIDE_BY_SIDE = -1;
export type SIDE_BY_SIDE_TYPE = typeof SIDE_BY_SIDE;
export interface IEditorInputWithOptions {
editor: IEditorInput;
options?: IEditorOptions;
}
export interface INextEditorService {
_serviceBrand: ServiceIdentifier<any>;
......@@ -107,8 +102,8 @@ export interface INextEditorService {
* active group. Use `SIDE_BY_SIDE` to open the editor in a new editor group to the side
* of the currently active group.
*/
openEditors(editors: IEditorInputWithOptions[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor>;
openEditors(editors: IResourceEditor[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor>;
openEditors(editors: IEditorInputWithOptions[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor[]>;
openEditors(editors: IResourceEditor[], group?: GroupIdentifier | SIDE_BY_SIDE_TYPE): Thenable<IEditor[]>;
/**
* Find out if the provided editor (or resource of an editor) is opened in any group.
......
......@@ -8,7 +8,7 @@
import { Event } from 'vs/base/common/event';
import { createDecorator, ServiceIdentifier, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { GroupIdentifier, IEditorOpeningEvent } from 'vs/workbench/common/editor';
import { IEditorInput, IEditor, IEditorOptions } from 'vs/platform/editor/common/editor';
import { IEditorInput, IEditor, IEditorOptions, IEditorInputWithOptions } from 'vs/platform/editor/common/editor';
export const INextEditorGroupsService = createDecorator<INextEditorGroupsService>('nextEditorGroupsService');
......@@ -25,6 +25,11 @@ export interface IMoveEditorOptions {
preserveFocus?: boolean;
}
export interface IAddGroupOptions {
activate?: boolean;
copyGroup?: boolean;
}
export interface ICopyEditorOptions extends IMoveEditorOptions { }
export interface INextEditorGroupsService {
......@@ -94,9 +99,9 @@ export interface INextEditorGroupsService {
*
* @param location the group from which to split to add a new group
* @param direction the direction of where to split to
* @param copyGroup optionally copy the editors of the group to add from
* @param options configure the newly group with options
*/
addGroup(location: INextEditorGroup | GroupIdentifier, direction: GroupDirection, copyGroup?: boolean): INextEditorGroup;
addGroup(location: INextEditorGroup | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): INextEditorGroup;
/**
* Remove a group from the editor area.
......@@ -193,6 +198,12 @@ export interface INextEditorGroup {
*/
openEditor(editor: IEditorInput, options?: IEditorOptions): Thenable<void>;
/**
* Opens editors in this group. The returned promise is resolved when the
* editor has finished loading.
*/
openEditors(editors: IEditorInputWithOptions[]): Thenable<void>;
/**
* Find out if the provided editor is opened in the group.
*
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册