未验证 提交 ca05440d 编写于 作者: S SteVen Batten 提交者: GitHub

Enable Drag and Drop for views (#90917)

* drag and drop views

* restructure for code re-use
上级 a50399df
......@@ -19,14 +19,14 @@ import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction } from 'vs/workb
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { CompositeBar, ICompositeBarItem } from 'vs/workbench/browser/parts/compositeBar';
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar';
import { Dimension, addClass, removeNode } from 'vs/base/browser/dom';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions';
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection } from 'vs/workbench/common/views';
import { IViewDescriptorService, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewDescriptorCollection, ViewContainerLocation } from 'vs/workbench/common/views';
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
......@@ -128,6 +128,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
getContextMenuActionsForComposite: () => [],
getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(),
hidePart: () => this.layoutService.setSideBarHidden(true),
dndHandler: new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Sidebar,
(id: string, focus?: boolean) => this.viewletService.openViewlet(id, focus),
(from: string, to: string) => this.compositeBar.move(from, to),
() => this.getPinnedViewletIds()
),
compositeSize: 50,
colors: (theme: ITheme) => this.getActivitybarItemColors(theme),
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
......
......@@ -20,6 +20,10 @@ import { isUndefinedOrNull } from 'vs/base/common/types';
import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
import { ITheme } from 'vs/platform/theme/common/themeService';
import { Emitter } from 'vs/base/common/event';
import { DraggedViewIdentifier } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IDragAndDropData } from 'vs/base/browser/dnd';
import { Registry } from 'vs/platform/registry/common/platform';
import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views';
export interface ICompositeBarItem {
id: string;
......@@ -29,12 +33,88 @@ export interface ICompositeBarItem {
visible: boolean;
}
export class CompositeDragAndDropData implements IDragAndDropData {
constructor(
private type: 'view' | 'composite',
private id: string
) { }
update(dataTransfer: DataTransfer): void {
// no-op
}
getData(): { type: 'view' | 'composite', id: string } {
return { type: this.type, id: this.id };
}
}
export interface ICompositeDragAndDrop {
drop(data: IDragAndDropData, target: string | undefined, originalEvent: DragEvent): void;
}
export class CompositeDragAndDrop implements ICompositeDragAndDrop {
constructor(
private viewDescriptorService: IViewDescriptorService,
private viewContainerLocation: ViewContainerLocation,
private openComposite: (id: string, focus?: boolean) => void,
private moveComposite: (from: string, to: string) => void,
private getVisibleCompositeIds: () => string[]
) { }
drop(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent): void {
const dragData = data.getData();
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
if (dragData.type === 'composite') {
const currentContainer = viewContainerRegistry.get(dragData.id)!;
const currentLocation = viewContainerRegistry.getViewContainerLocation(currentContainer);
if (targetCompositeId) {
if (currentLocation !== this.viewContainerLocation && this.viewContainerLocation !== ViewContainerLocation.Panel) {
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
if (destinationContainer) {
this.viewDescriptorService.moveViewsToContainer(this.viewDescriptorService.getViewDescriptors(currentContainer)!.allViewDescriptors.filter(vd => vd.canMoveView), destinationContainer);
this.openComposite(targetCompositeId, true);
}
} else {
this.moveComposite(dragData.id, targetCompositeId);
}
}
} else {
const viewDescriptor = this.viewDescriptorService.getViewDescriptor(dragData.id);
if (viewDescriptor && viewDescriptor.canMoveView) {
if (targetCompositeId) {
const destinationContainer = viewContainerRegistry.get(targetCompositeId);
if (destinationContainer) {
if (this.viewContainerLocation === ViewContainerLocation.Sidebar) {
this.viewDescriptorService.moveViewsToContainer([viewDescriptor], destinationContainer);
this.openComposite(targetCompositeId, true);
} else {
this.viewDescriptorService.moveViewToLocation(viewDescriptor, this.viewContainerLocation);
this.moveComposite(this.viewDescriptorService.getViewContainer(viewDescriptor.id)!.id, targetCompositeId);
}
}
} else {
this.viewDescriptorService.moveViewToLocation(viewDescriptor, this.viewContainerLocation);
const newCompositeId = this.viewDescriptorService.getViewContainer(dragData.id)!.id;
const visibleItems = this.getVisibleCompositeIds();
const targetId = visibleItems.length ? visibleItems[visibleItems.length - 1] : undefined;
if (targetId && targetId !== newCompositeId) {
this.moveComposite(newCompositeId, targetId);
}
this.openComposite(newCompositeId, true);
}
}
}
}
}
export interface ICompositeBarOptions {
readonly icon: boolean;
readonly orientation: ActionsOrientation;
readonly colors: (theme: ITheme) => ICompositeBarColors;
readonly compositeSize: number;
readonly overflowActionSize: number;
readonly dndHandler: ICompositeDragAndDrop;
getActivityAction: (compositeId: string) => ActivityAction;
getCompositePinnedAction: (compositeId: string) => Action;
......@@ -58,7 +138,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
private visibleComposites: string[];
private compositeSizeInBar: Map<string, number>;
private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier>;
private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier | DraggedViewIdentifier>;
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChange = this._onDidChange.event;
......@@ -107,6 +187,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
() => this.getContextMenuActions() as Action[],
this.options.colors,
this.options.icon,
this.options.dndHandler,
this
);
},
......@@ -134,6 +215,16 @@ export class CompositeBar extends Widget implements ICompositeBar {
}
}
}
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) {
const draggedViewId = data[0].id;
this.compositeTransfer.clearData(DraggedViewIdentifier.prototype);
this.options.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), undefined, e);
}
}
}));
return actionBarDiv;
......
......@@ -20,6 +20,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { Emitter } from 'vs/base/common/event';
import { DragAndDropObserver, LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
import { Color } from 'vs/base/common/color';
import { ICompositeDragAndDrop, CompositeDragAndDropData } from 'vs/workbench/browser/parts/compositeBar';
import { DraggedViewIdentifier } from 'vs/workbench/browser/parts/views/viewPaneContainer';
export interface ICompositeActivity {
badge: IBadge;
......@@ -458,7 +460,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
private static manageExtensionAction: ManageExtensionAction;
private compositeActivity: IActivity | undefined;
private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier>;
private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier | DraggedViewIdentifier>;
constructor(
private compositeActivityAction: ActivityAction,
......@@ -467,6 +469,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
private contextMenuActionsProvider: () => ReadonlyArray<Action>,
colors: (theme: ITheme) => ICompositeBarColors,
icon: boolean,
private dndHandler: ICompositeDragAndDrop,
private compositeBar: ICompositeBar,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
......@@ -475,7 +478,7 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
) {
super(compositeActivityAction, { draggable: true, colors, icon }, themeService);
this.compositeTransfer = LocalSelectionTransfer.getInstance<DraggedCompositeIdentifier>();
this.compositeTransfer = LocalSelectionTransfer.getInstance<DraggedCompositeIdentifier | DraggedViewIdentifier>();
if (!CompositeActionViewItem.manageExtensionAction) {
CompositeActionViewItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
......@@ -571,16 +574,27 @@ export class CompositeActionViewItem extends ActivityActionViewItem {
this.updateFromDragging(container, false);
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
this.compositeBar.move(draggedCompositeId, this.activity.id);
this.dndHandler.drop(new CompositeDragAndDropData('composite', draggedCompositeId), this.activity.id, e);
}
}
}
if (this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) {
const data = this.compositeTransfer.getData(DraggedViewIdentifier.prototype);
if (Array.isArray(data)) {
const draggedViewId = data[0].id;
this.dndHandler.drop(new CompositeDragAndDropData('view', draggedViewId), this.activity.id, e);
}
}
}
}));
// Activate on drag over to reveal targets
[this.badge, this.label].forEach(b => this._register(new DelayedDragHandler(b, () => {
if (!this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && !this.getAction().checked) {
if (!(this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) ||
this.compositeTransfer.hasData(DraggedViewIdentifier.prototype)) &&
!this.getAction().checked) {
this.getAction().run();
}
})));
......
......@@ -22,7 +22,7 @@ import { ClosePanelAction, PanelActivityAction, ToggleMaximizedPanelAction, Togg
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BACKGROUND, PANEL_INPUT_BORDER } from 'vs/workbench/common/theme';
import { activeContrastBorder, focusBorder, contrastBorder, editorBackground, badgeBackground, badgeForeground } from 'vs/platform/theme/common/colorRegistry';
import { CompositeBar, ICompositeBarItem } from 'vs/workbench/browser/parts/compositeBar';
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar';
import { ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions';
import { IBadge } from 'vs/workbench/services/activity/common/activity';
import { INotificationService } from 'vs/platform/notification/common/notification';
......@@ -33,7 +33,7 @@ import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/con
import { isUndefinedOrNull, assertIsDefined } from 'vs/base/common/types';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection } from 'vs/workbench/common/views';
import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, IViewDescriptorService, IViewDescriptorCollection, ViewContainerLocation } from 'vs/workbench/common/views';
import { MenuId } from 'vs/platform/actions/common/actions';
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
......@@ -142,6 +142,11 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
getContextMenuActionsForComposite: (compositeId: string) => this.getContextMenuActionsForComposite(compositeId) as Action[],
getDefaultCompositeId: () => this.panelRegistry.getDefaultPanelId(),
hidePart: () => this.layoutService.setPanelHidden(true),
dndHandler: new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel,
(id: string, focus?: boolean) => this.openPanel(id, focus),
(from: string, to: string) => this.compositeBar.move(from, to),
() => this.getPinnedPanels().map(p => p.id)
),
compositeSize: 0,
overflowActionSize: 44,
colors: (theme: ITheme) => ({
......@@ -397,7 +402,17 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
getPanels(): readonly PanelDescriptor[] {
return this.panelRegistry.getPanels()
.sort((v1, v2) => typeof v1.order === 'number' && typeof v2.order === 'number' ? v1.order - v2.order : NaN);
.sort((v1, v2) => {
if (typeof v1.order !== 'number') {
return 1;
}
if (typeof v2.order !== 'number') {
return -1;
}
return v1.order - v2.order;
});
}
getPinnedPanels(): readonly PanelDescriptor[] {
......
......@@ -42,6 +42,7 @@ import { parseLinkedText } from 'vs/base/common/linkedText';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { Button } from 'vs/base/browser/ui/button/button';
import { Link } from 'vs/platform/opener/browser/link';
import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
export interface IPaneColors extends IColorMapping {
dropBackground?: ColorIdentifier;
......@@ -57,6 +58,15 @@ export interface IViewPaneOptions extends IPaneOptions {
titleMenuId?: MenuId;
}
export class DraggedViewIdentifier {
constructor(private _viewId: string) { }
get id(): string {
return this._viewId;
}
}
const viewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
interface IItem {
......@@ -444,6 +454,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
private paneItems: IViewPaneItem[] = [];
private paneview?: PaneView;
private static viewTransfer = LocalSelectionTransfer.getInstance<DraggedViewIdentifier>();
private visible: boolean = false;
private areExtensionsReady: boolean = false;
......@@ -874,6 +886,22 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
this.paneItems.splice(index, 0, paneItem);
assertIsDefined(this.paneview).addPane(pane, size, index);
this._register(addDisposableListener(pane.draggableElement, EventType.DRAG_START, (e: DragEvent) => {
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'move';
}
// Register as dragged to local transfer
ViewPaneContainer.viewTransfer.setData([new DraggedViewIdentifier(pane.id)], DraggedViewIdentifier.prototype);
}));
this._register(addDisposableListener(pane.draggableElement, EventType.DRAG_END, (e: DragEvent) => {
if (ViewPaneContainer.viewTransfer.hasData(DraggedViewIdentifier.prototype)) {
ViewPaneContainer.viewTransfer.clearData(DraggedViewIdentifier.prototype);
}
}));
}
removePanes(panes: ViewPane[]): void {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册