Skip to content

Commit 5963a8e

Browse files
authored
Enabled movable Custom Views (microsoft#89240)
Enabled movable Custom Views
2 parents f22d298 + 1c589bd commit 5963a8e

7 files changed

Lines changed: 75 additions & 21 deletions

File tree

src/vs/platform/list/browser/listService.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,11 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
807807
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
808808
this.disposables.add(this.internals);
809809
}
810+
811+
updateOptions(options: IWorkbenchAsyncDataTreeOptions<T, TFilterData> = {}): void {
812+
super.updateOptions(options);
813+
this.internals.updateStyleOverrides(options.overrideStyles);
814+
}
810815
}
811816

812817
export interface IWorkbenchAsyncDataTreeOptions<T, TFilterData> extends IAsyncDataTreeOptions<T, TFilterData> {
@@ -839,6 +844,11 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
839844
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
840845
this.disposables.add(this.internals);
841846
}
847+
848+
updateOptions(options: IWorkbenchAsyncDataTreeOptions<T, TFilterData> = {}): void {
849+
super.updateOptions(options);
850+
this.internals.updateStyleOverrides(options.overrideStyles);
851+
}
842852
}
843853

844854
export interface IWorkbenchCompressibleAsyncDataTreeOptions<T, TFilterData> extends ICompressibleAsyncDataTreeOptions<T, TFilterData> {
@@ -936,15 +946,16 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
936946
private hasMultiSelection: IContextKey<boolean>;
937947
private _useAltAsMultipleSelectionModifier: boolean;
938948
private disposables: IDisposable[] = [];
949+
private styler!: IDisposable;
939950

940951
constructor(
941-
tree: WorkbenchObjectTree<T, TFilterData> | CompressibleObjectTree<T, TFilterData> | WorkbenchDataTree<TInput, T, TFilterData> | WorkbenchAsyncDataTree<TInput, T, TFilterData> | WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData>,
952+
private tree: WorkbenchObjectTree<T, TFilterData> | CompressibleObjectTree<T, TFilterData> | WorkbenchDataTree<TInput, T, TFilterData> | WorkbenchAsyncDataTree<TInput, T, TFilterData> | WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData>,
942953
options: IAbstractTreeOptions<T, TFilterData> | IAsyncDataTreeOptions<T, TFilterData>,
943954
getAutomaticKeyboardNavigation: () => boolean | undefined,
944955
overrideStyles: IColorMapping | undefined,
945956
@IContextKeyService contextKeyService: IContextKeyService,
946957
@IListService listService: IListService,
947-
@IThemeService themeService: IThemeService,
958+
@IThemeService private themeService: IThemeService,
948959
@IConfigurationService configurationService: IConfigurationService,
949960
@IAccessibilityService accessibilityService: IAccessibilityService,
950961
) {
@@ -970,10 +981,11 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
970981
});
971982
};
972983

984+
this.updateStyleOverrides(overrideStyles);
985+
973986
this.disposables.push(
974987
this.contextKeyService,
975988
(listService as ListService).register(tree),
976-
overrideStyles ? attachListStyler(tree, themeService, overrideStyles) : Disposable.None,
977989
tree.onDidChangeSelection(() => {
978990
const selection = tree.getSelection();
979991
const focus = tree.getFocus();
@@ -1023,8 +1035,14 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
10231035
return this._useAltAsMultipleSelectionModifier;
10241036
}
10251037

1038+
updateStyleOverrides(overrideStyles?: IColorMapping): void {
1039+
dispose(this.styler);
1040+
this.styler = overrideStyles ? attachListStyler(this.tree, this.themeService, overrideStyles) : Disposable.None;
1041+
}
1042+
10261043
dispose(): void {
10271044
this.disposables = dispose(this.disposables);
1045+
this.styler = dispose(this.styler);
10281046
}
10291047
}
10301048

src/vs/workbench/api/browser/viewsExtensionPoint.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,9 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
399399
ctorDescriptor: new SyncDescriptor(CustomTreeViewPane),
400400
when: ContextKeyExpr.deserialize(item.when),
401401
canToggleVisibility: true,
402+
canMoveView: true,
403+
treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name),
402404
collapsed: this.showCollapsed(container),
403-
treeView: this.instantiationService.createInstance(CustomTreeView, item.id, item.name, container),
404405
order: order,
405406
extensionId: extension.description.identifier,
406407
originalContainerId: entry.key,

src/vs/workbench/browser/parts/views/customView.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
1313
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
1414
import { ContextAwareMenuEntryActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
1515
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
16-
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions, IViewDescriptorService } from 'vs/workbench/common/views';
16+
import { ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ITreeItemLabel, Extensions, IViewDescriptorService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views';
1717
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
1818
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1919
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -41,7 +41,7 @@ import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } fro
4141
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
4242
import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults';
4343
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
44-
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
44+
import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme';
4545

4646
export class CustomTreeViewPane extends ViewPane {
4747

@@ -154,7 +154,6 @@ export class CustomTreeView extends Disposable implements ITreeView {
154154
constructor(
155155
private id: string,
156156
private _title: string,
157-
private viewContainer: ViewContainer,
158157
@IExtensionService private readonly extensionService: IExtensionService,
159158
@IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService,
160159
@IInstantiationService private readonly instantiationService: IInstantiationService,
@@ -163,7 +162,8 @@ export class CustomTreeView extends Disposable implements ITreeView {
163162
@IProgressService private readonly progressService: IProgressService,
164163
@IContextMenuService private readonly contextMenuService: IContextMenuService,
165164
@IKeybindingService private readonly keybindingService: IKeybindingService,
166-
@INotificationService private readonly notificationService: INotificationService
165+
@INotificationService private readonly notificationService: INotificationService,
166+
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService
167167
) {
168168
super();
169169
this.root = new Root();
@@ -174,14 +174,23 @@ export class CustomTreeView extends Disposable implements ITreeView {
174174
this.doRefresh([this.root]); /** soft refresh **/
175175
}
176176
}));
177-
this._register(Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).onDidChangeContainer(({ views, from, to }) => {
178-
if (from === this.viewContainer && views.some(v => v.id === this.id)) {
179-
this.viewContainer = to;
177+
this._register(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => {
178+
if (views.some(v => v.id === this.id)) {
179+
this.tree?.updateOptions({ overrideStyles: { listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND } });
180180
}
181181
}));
182+
182183
this.create();
183184
}
184185

186+
get viewContainer(): ViewContainer {
187+
return this.viewDescriptorService.getViewContainer(this.id)!;
188+
}
189+
190+
get viewLocation(): ViewContainerLocation {
191+
return this.viewDescriptorService.getViewLocation(this.id)!;
192+
}
193+
185194
private _dataProvider: ITreeViewDataProvider | undefined;
186195
get dataProvider(): ITreeViewDataProvider | undefined {
187196
return this._dataProvider;
@@ -360,7 +369,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
360369
},
361370
multipleSelectionSupport: this.canSelectMany,
362371
overrideStyles: {
363-
listBackground: SIDE_BAR_BACKGROUND
372+
listBackground: this.viewLocation === ViewContainerLocation.Sidebar ? SIDE_BAR_BACKGROUND : PANEL_BACKGROUND
364373
}
365374
}) as WorkbenchAsyncDataTree<ITreeItem, ITreeItem, FuzzyScore>);
366375
aligner.tree = this.tree;

src/vs/workbench/browser/parts/views/viewPaneContainer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export abstract class ViewPane extends Pane implements IView {
9191
@IContextMenuService protected contextMenuService: IContextMenuService,
9292
@IConfigurationService protected readonly configurationService: IConfigurationService,
9393
@IContextKeyService contextKeyService: IContextKeyService,
94-
@IViewDescriptorService private viewDescriptorService: IViewDescriptorService,
94+
@IViewDescriptorService protected viewDescriptorService: IViewDescriptorService,
9595
@IInstantiationService protected instantiationService: IInstantiationService,
9696
) {
9797
super(options);

src/vs/workbench/common/views.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -349,10 +349,8 @@ export interface IViewsViewlet extends IViewlet {
349349

350350
}
351351

352-
export const IViewDescriptorService = createDecorator<IViewDescriptorService>('viewDescriptorService');
353352
export const IViewsService = createDecorator<IViewsService>('viewsService');
354353

355-
356354
export interface IViewsService {
357355
_serviceBrand: undefined;
358356

@@ -361,11 +359,15 @@ export interface IViewsService {
361359
openView(id: string, focus?: boolean): Promise<IView | null>;
362360
}
363361

362+
export const IViewDescriptorService = createDecorator<IViewDescriptorService>('viewDescriptorService');
364363

365364
export interface IViewDescriptorService {
366365

367366
_serviceBrand: undefined;
368367

368+
readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>;
369+
readonly onDidChangeLocation: Event<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }>;
370+
369371
moveViewToLocation(view: IViewDescriptor, location: ViewContainerLocation): void;
370372

371373
moveViewsToContainer(views: IViewDescriptor[], viewContainer: ViewContainer): void;
@@ -443,9 +445,7 @@ export interface IRevealOptions {
443445
}
444446

445447
export interface ITreeViewDescriptor extends IViewDescriptor {
446-
447-
readonly treeView: ITreeView;
448-
448+
treeView: ITreeView;
449449
}
450450

451451
export type TreeViewItemHandleArg = {

src/vs/workbench/contrib/outline/browser/outlinePane.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ import { basename } from 'vs/base/common/resources';
4747
import { IDataSource } from 'vs/base/browser/ui/tree/tree';
4848
import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService';
4949
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
50-
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
5150
import { IViewDescriptorService } from 'vs/workbench/common/views';
5251

5352
class RequestState {
@@ -335,13 +334,19 @@ export class OutlinePane extends ViewPane {
335334
keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(),
336335
hideTwistiesOfChildlessElements: true,
337336
overrideStyles: {
338-
listBackground: SIDE_BAR_BACKGROUND
337+
listBackground: this.getBackgroundColor()
339338
}
340339
}
341340
);
342341

342+
343343
this._disposables.push(this._tree);
344344
this._disposables.push(this._outlineViewState.onDidChange(this._onDidChangeUserState, this));
345+
this._disposables.push(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => {
346+
if (views.some(v => v.id === this.id)) {
347+
this._tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } });
348+
}
349+
}));
345350

346351
// override the globally defined behaviour
347352
this._tree.updateOptions({

src/vs/workbench/services/views/browser/viewDescriptorService.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
181181
private static readonly CACHED_VIEW_POSITIONS = 'views.cachedViewPositions';
182182
private static readonly COMMON_CONTAINER_ID_PREFIX = 'workbench.views.service';
183183

184+
private readonly _onDidChangeContainer: Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>());
185+
readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._onDidChangeContainer.event;
186+
187+
private readonly _onDidChangeLocation: Emitter<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }> = this._register(new Emitter<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }>());
188+
readonly onDidChangeLocation: Event<{ views: IViewDescriptor[], from: ViewContainerLocation, to: ViewContainerLocation }> = this._onDidChangeLocation.event;
189+
184190
private readonly viewDescriptorCollections: Map<ViewContainer, { viewDescriptorCollection: ViewDescriptorCollection, disposable: IDisposable; }>;
185191
private readonly activeViewContextKeys: Map<string, IContextKey<boolean>>;
186192
private readonly movableViewContextKeys: Map<string, IContextKey<boolean>>;
@@ -230,7 +236,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
230236
this._register(this.viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(views, viewContainer)));
231237
this._register(this.viewsRegistry.onViewsDeregistered(({ views, viewContainer }) => this.onDidDeregisterViews(views, viewContainer)));
232238

233-
this._register(this.viewsRegistry.onDidChangeContainer(({ views, from, to }) => { this.removeViews(from, views); this.addViews(to, views); }));
239+
this._register(this.viewsRegistry.onDidChangeContainer(({ views, from, to }) => { this.removeViews(from, views); this.addViews(to, views); this._onDidChangeContainer.fire({ views, from, to }); }));
234240

235241
this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onDidRegisterViewContainer(viewContainer)));
236242
this._register(this.viewContainersRegistry.onDidDeregister(({ viewContainer }) => this.onDidDeregisterViewContainer(viewContainer)));
@@ -295,6 +301,11 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
295301
const viewDescriptor = this.getViewDescriptor(viewId);
296302
if (viewContainer && viewDescriptor) {
297303
this.addViews(viewContainer, [viewDescriptor]);
304+
305+
const newLocation = this.viewContainersRegistry.getViewContainerLocation(viewContainer)!;
306+
if (containerInfo.location && containerInfo.location !== newLocation) {
307+
this._onDidChangeLocation.fire({ views: [viewDescriptor], from: containerInfo.location, to: newLocation });
308+
}
298309
}
299310
}
300311

@@ -357,6 +368,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
357368

358369
getViewContainer(viewId: string): ViewContainer | null {
359370
const containerId = this.cachedViewInfo.get(viewId)?.containerId;
371+
360372
return containerId ?
361373
this.viewContainersRegistry.get(containerId) ?? null :
362374
this.viewsRegistry.getViewContainer(viewId);
@@ -395,6 +407,15 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
395407
if (from && to && from !== to) {
396408
this.removeViews(from, views);
397409
this.addViews(to, views);
410+
411+
const oldLocation = this.viewContainersRegistry.getViewContainerLocation(from)!;
412+
const newLocation = this.viewContainersRegistry.getViewContainerLocation(to)!;
413+
414+
if (oldLocation !== newLocation) {
415+
this._onDidChangeLocation.fire({ views, from: oldLocation, to: newLocation });
416+
}
417+
418+
this._onDidChangeContainer.fire({ views, from, to });
398419
this.saveViewPositionsToCache();
399420
}
400421
}

0 commit comments

Comments
 (0)