Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import './media/sidebarpart.css';
7
import './sidebarActions.js';
8
import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, Parts, Position as SideBarPosition } from '../../../services/layout/browser/layoutService.js';
9
import { SidebarFocusContext, ActiveViewletContext } from '../../../common/contextkeys.js';
10
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
11
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
12
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
13
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
14
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
15
import { contrastBorder } from '../../../../platform/theme/common/colorRegistry.js';
16
import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_TITLE_BORDER, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER, SIDE_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_TOP_FOREGROUND, ACTIVITY_BAR_TOP_ACTIVE_BORDER, ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND, ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER } from '../../../common/theme.js';
17
import { INotificationService } from '../../../../platform/notification/common/notification.js';
18
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
19
import { AnchorAlignment } from '../../../../base/browser/ui/contextview/contextview.js';
20
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
21
import { LayoutPriority } from '../../../../base/browser/ui/grid/grid.js';
22
import { assertReturnsDefined } from '../../../../base/common/types.js';
23
import { IViewDescriptorService } from '../../../common/views.js';
24
import { AbstractPaneCompositePart, CompositeBarPosition } from '../paneCompositePart.js';
25
import { ActivityBarCompositeBar, ActivitybarPart } from '../activitybar/activitybarPart.js';
26
import { ActionsOrientation } from '../../../../base/browser/ui/actionbar/actionbar.js';
27
import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js';
28
import { IPaneCompositeBarOptions } from '../paneCompositeBar.js';
29
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
30
import { Action2, IMenuService, registerAction2 } from '../../../../platform/actions/common/actions.js';
31
import { Separator } from '../../../../base/common/actions.js';
32
import { ToggleActivityBarVisibilityActionId } from '../../actions/layoutActions.js';
33
import { localize2 } from '../../../../nls.js';
34
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
35
36
export class SidebarPart extends AbstractPaneCompositePart {
37
38
static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid';
39
40
//#region IView
41
42
readonly minimumWidth: number = 170;
43
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
44
readonly minimumHeight: number = 0;
45
readonly maximumHeight: number = Number.POSITIVE_INFINITY;
46
override get snap(): boolean { return true; }
47
48
readonly priority: LayoutPriority = LayoutPriority.Low;
49
50
get preferredWidth(): number | undefined {
51
const viewlet = this.getActivePaneComposite();
52
53
if (!viewlet) {
54
return;
55
}
56
57
const width = viewlet.getOptimalWidth();
58
if (typeof width !== 'number') {
59
return;
60
}
61
62
return Math.max(width, 300);
63
}
64
65
private readonly activityBarPart = this._register(this.instantiationService.createInstance(ActivitybarPart, this));
66
67
//#endregion
68
69
constructor(
70
@INotificationService notificationService: INotificationService,
71
@IStorageService storageService: IStorageService,
72
@IContextMenuService contextMenuService: IContextMenuService,
73
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
74
@IKeybindingService keybindingService: IKeybindingService,
75
@IHoverService hoverService: IHoverService,
76
@IInstantiationService instantiationService: IInstantiationService,
77
@IThemeService themeService: IThemeService,
78
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
79
@IContextKeyService contextKeyService: IContextKeyService,
80
@IExtensionService extensionService: IExtensionService,
81
@IConfigurationService private readonly configurationService: IConfigurationService,
82
@IMenuService menuService: IMenuService,
83
) {
84
super(
85
Parts.SIDEBAR_PART,
86
{ hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 },
87
SidebarPart.activeViewletSettingsKey,
88
ActiveViewletContext.bindTo(contextKeyService),
89
SidebarFocusContext.bindTo(contextKeyService),
90
'sideBar',
91
'viewlet',
92
SIDE_BAR_TITLE_FOREGROUND,
93
SIDE_BAR_TITLE_BORDER,
94
notificationService,
95
storageService,
96
contextMenuService,
97
layoutService,
98
keybindingService,
99
hoverService,
100
instantiationService,
101
themeService,
102
viewDescriptorService,
103
contextKeyService,
104
extensionService,
105
menuService,
106
);
107
108
this.rememberActivityBarVisiblePosition();
109
this._register(configurationService.onDidChangeConfiguration(e => {
110
if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION)) {
111
this.onDidChangeActivityBarLocation();
112
}
113
}));
114
115
this.registerActions();
116
}
117
118
private onDidChangeActivityBarLocation(): void {
119
this.activityBarPart.hide();
120
121
this.updateCompositeBar();
122
123
const id = this.getActiveComposite()?.getId();
124
if (id) {
125
this.onTitleAreaUpdate(id);
126
}
127
128
if (this.shouldShowActivityBar()) {
129
this.activityBarPart.show();
130
}
131
132
this.rememberActivityBarVisiblePosition();
133
}
134
135
override updateStyles(): void {
136
super.updateStyles();
137
138
const container = assertReturnsDefined(this.getContainer());
139
140
container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND) || '';
141
container.style.color = this.getColor(SIDE_BAR_FOREGROUND) || '';
142
143
const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder);
144
const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT;
145
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '';
146
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : '';
147
container.style.borderRightColor = isPositionLeft ? borderColor || '' : '';
148
container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : '';
149
container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : '';
150
container.style.borderLeftColor = !isPositionLeft ? borderColor || '' : '';
151
container.style.outlineColor = this.getColor(SIDE_BAR_DRAG_AND_DROP_BACKGROUND) ?? '';
152
}
153
154
override layout(width: number, height: number, top: number, left: number): void {
155
if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
156
return;
157
}
158
159
super.layout(width, height, top, left);
160
}
161
162
protected override getTitleAreaDropDownAnchorAlignment(): AnchorAlignment {
163
return this.layoutService.getSideBarPosition() === SideBarPosition.LEFT ? AnchorAlignment.LEFT : AnchorAlignment.RIGHT;
164
}
165
166
protected override createCompositeBar(): ActivityBarCompositeBar {
167
return this.instantiationService.createInstance(ActivityBarCompositeBar, this.getCompositeBarOptions(), this.partId, this, false);
168
}
169
170
protected getCompositeBarOptions(): IPaneCompositeBarOptions {
171
return {
172
partContainerClass: 'sidebar',
173
pinnedViewContainersKey: ActivitybarPart.pinnedViewContainersKey,
174
placeholderViewContainersKey: ActivitybarPart.placeholderViewContainersKey,
175
viewContainersWorkspaceStateKey: ActivitybarPart.viewContainersWorkspaceStateKey,
176
icon: true,
177
orientation: ActionsOrientation.HORIZONTAL,
178
recomputeSizes: true,
179
activityHoverOptions: {
180
position: () => this.getCompositeBarPosition() === CompositeBarPosition.BOTTOM ? HoverPosition.ABOVE : HoverPosition.BELOW,
181
},
182
fillExtraContextMenuActions: actions => {
183
if (this.getCompositeBarPosition() === CompositeBarPosition.TITLE) {
184
const viewsSubmenuAction = this.getViewsSubmenuAction();
185
if (viewsSubmenuAction) {
186
actions.push(new Separator());
187
actions.push(viewsSubmenuAction);
188
}
189
}
190
},
191
compositeSize: 0,
192
iconSize: 16,
193
overflowActionSize: 30,
194
colors: theme => ({
195
activeBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),
196
inactiveBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),
197
activeBorderBottomColor: theme.getColor(ACTIVITY_BAR_TOP_ACTIVE_BORDER),
198
activeForegroundColor: theme.getColor(ACTIVITY_BAR_TOP_FOREGROUND),
199
inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND),
200
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
201
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
202
dragAndDropBorder: theme.getColor(ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER)
203
}),
204
compact: true
205
};
206
}
207
208
protected shouldShowCompositeBar(): boolean {
209
const activityBarPosition = this.configurationService.getValue<ActivityBarPosition>(LayoutSettings.ACTIVITY_BAR_LOCATION);
210
return activityBarPosition === ActivityBarPosition.TOP || activityBarPosition === ActivityBarPosition.BOTTOM;
211
}
212
213
private shouldShowActivityBar(): boolean {
214
if (this.shouldShowCompositeBar()) {
215
return false;
216
}
217
218
return this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) !== ActivityBarPosition.HIDDEN;
219
}
220
221
protected getCompositeBarPosition(): CompositeBarPosition {
222
const activityBarPosition = this.configurationService.getValue<ActivityBarPosition>(LayoutSettings.ACTIVITY_BAR_LOCATION);
223
switch (activityBarPosition) {
224
case ActivityBarPosition.TOP: return CompositeBarPosition.TOP;
225
case ActivityBarPosition.BOTTOM: return CompositeBarPosition.BOTTOM;
226
case ActivityBarPosition.HIDDEN:
227
case ActivityBarPosition.DEFAULT: // noop
228
default: return CompositeBarPosition.TITLE;
229
}
230
}
231
232
private rememberActivityBarVisiblePosition(): void {
233
const activityBarPosition = this.configurationService.getValue<string>(LayoutSettings.ACTIVITY_BAR_LOCATION);
234
if (activityBarPosition !== ActivityBarPosition.HIDDEN) {
235
this.storageService.store(LayoutSettings.ACTIVITY_BAR_LOCATION, activityBarPosition, StorageScope.PROFILE, StorageTarget.USER);
236
}
237
}
238
239
private getRememberedActivityBarVisiblePosition(): ActivityBarPosition {
240
const activityBarPosition = this.storageService.get(LayoutSettings.ACTIVITY_BAR_LOCATION, StorageScope.PROFILE);
241
switch (activityBarPosition) {
242
case ActivityBarPosition.TOP: return ActivityBarPosition.TOP;
243
case ActivityBarPosition.BOTTOM: return ActivityBarPosition.BOTTOM;
244
default: return ActivityBarPosition.DEFAULT;
245
}
246
}
247
248
override getPinnedPaneCompositeIds(): string[] {
249
return this.shouldShowCompositeBar() ? super.getPinnedPaneCompositeIds() : this.activityBarPart.getPinnedPaneCompositeIds();
250
}
251
252
override getVisiblePaneCompositeIds(): string[] {
253
return this.shouldShowCompositeBar() ? super.getVisiblePaneCompositeIds() : this.activityBarPart.getVisiblePaneCompositeIds();
254
}
255
256
override getPaneCompositeIds(): string[] {
257
return this.shouldShowCompositeBar() ? super.getPaneCompositeIds() : this.activityBarPart.getPaneCompositeIds();
258
}
259
260
async focusActivityBar(): Promise<void> {
261
if (this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.HIDDEN) {
262
await this.configurationService.updateValue(LayoutSettings.ACTIVITY_BAR_LOCATION, this.getRememberedActivityBarVisiblePosition());
263
264
this.onDidChangeActivityBarLocation();
265
}
266
267
if (this.shouldShowCompositeBar()) {
268
this.focusCompositeBar();
269
} else {
270
if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) {
271
this.layoutService.setPartHidden(false, Parts.ACTIVITYBAR_PART);
272
}
273
274
this.activityBarPart.show(true);
275
}
276
}
277
278
private registerActions(): void {
279
const that = this;
280
this._register(registerAction2(class extends Action2 {
281
constructor() {
282
super({
283
id: ToggleActivityBarVisibilityActionId,
284
title: localize2('toggleActivityBar', "Toggle Activity Bar Visibility"),
285
});
286
}
287
run(): Promise<void> {
288
const value = that.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.HIDDEN ? that.getRememberedActivityBarVisiblePosition() : ActivityBarPosition.HIDDEN;
289
return that.configurationService.updateValue(LayoutSettings.ACTIVITY_BAR_LOCATION, value);
290
}
291
}));
292
}
293
294
toJSON(): object {
295
return {
296
type: Parts.SIDEBAR_PART
297
};
298
}
299
}
300
301