Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts
5334 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/auxiliaryBarPart.css';
7
import { localize } from '../../../../nls.js';
8
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
9
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
10
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
11
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
12
import { INotificationService } from '../../../../platform/notification/common/notification.js';
13
import { IStorageService } from '../../../../platform/storage/common/storage.js';
14
import { contrastBorder } from '../../../../platform/theme/common/colorRegistry.js';
15
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
16
import { ActiveAuxiliaryContext, AuxiliaryBarFocusContext } from '../../../common/contextkeys.js';
17
import { ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_TOP_ACTIVE_BORDER, ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER, ACTIVITY_BAR_TOP_FOREGROUND, ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_DRAG_AND_DROP_BORDER, PANEL_INACTIVE_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_BORDER, SIDE_BAR_TITLE_BORDER, SIDE_BAR_FOREGROUND } from '../../../common/theme.js';
18
import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js';
19
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
20
import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, Parts, Position } from '../../../services/layout/browser/layoutService.js';
21
import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js';
22
import { IAction, Separator, SubmenuAction, toAction } from '../../../../base/common/actions.js';
23
import { ToggleAuxiliaryBarAction } from './auxiliaryBarActions.js';
24
import { assertReturnsDefined } from '../../../../base/common/types.js';
25
import { LayoutPriority } from '../../../../base/browser/ui/splitview/splitview.js';
26
import { ToggleSidebarPositionAction } from '../../actions/layoutActions.js';
27
import { ICommandService } from '../../../../platform/commands/common/commands.js';
28
import { AbstractPaneCompositePart, CompositeBarPosition } from '../paneCompositePart.js';
29
import { ActionsOrientation } from '../../../../base/browser/ui/actionbar/actionbar.js';
30
import { IPaneCompositeBarOptions } from '../paneCompositeBar.js';
31
import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js';
32
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
33
import { getContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js';
34
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
35
import { VisibleViewContainersTracker } from '../visibleViewContainersTracker.js';
36
import { Extensions } from '../../panecomposite.js';
37
38
interface IAuxiliaryBarPartConfiguration {
39
position: ActivityBarPosition;
40
41
canShowLabels: boolean;
42
showLabels: boolean;
43
}
44
45
export class AuxiliaryBarPart extends AbstractPaneCompositePart {
46
47
static readonly activeViewSettingsKey = 'workbench.auxiliarybar.activepanelid';
48
static readonly pinnedViewsKey = 'workbench.auxiliarybar.pinnedPanels';
49
static readonly placeholdeViewContainersKey = 'workbench.auxiliarybar.placeholderPanels';
50
static readonly viewContainersWorkspaceStateKey = 'workbench.auxiliarybar.viewContainersWorkspaceState';
51
52
// Use the side bar dimensions
53
override readonly minimumWidth: number = 170;
54
override readonly maximumWidth: number = Number.POSITIVE_INFINITY;
55
override readonly minimumHeight: number = 0;
56
override readonly maximumHeight: number = Number.POSITIVE_INFINITY;
57
58
get preferredHeight(): number | undefined {
59
// Don't worry about titlebar or statusbar visibility
60
// The difference is minimal and keeps this function clean
61
return this.layoutService.mainContainerDimension.height * 0.4;
62
}
63
64
get preferredWidth(): number | undefined {
65
const activeComposite = this.getActivePaneComposite();
66
67
if (!activeComposite) {
68
return undefined;
69
}
70
71
const width = activeComposite.getOptimalWidth();
72
if (typeof width !== 'number') {
73
return undefined;
74
}
75
76
return Math.max(width, 300);
77
}
78
79
readonly priority = LayoutPriority.Low;
80
81
private configuration: IAuxiliaryBarPartConfiguration;
82
private readonly visibleViewContainersTracker: VisibleViewContainersTracker;
83
84
constructor(
85
@INotificationService notificationService: INotificationService,
86
@IStorageService storageService: IStorageService,
87
@IContextMenuService contextMenuService: IContextMenuService,
88
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
89
@IKeybindingService keybindingService: IKeybindingService,
90
@IHoverService hoverService: IHoverService,
91
@IInstantiationService instantiationService: IInstantiationService,
92
@IThemeService themeService: IThemeService,
93
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
94
@IContextKeyService contextKeyService: IContextKeyService,
95
@IExtensionService extensionService: IExtensionService,
96
@ICommandService private commandService: ICommandService,
97
@IMenuService menuService: IMenuService,
98
@IConfigurationService private readonly configurationService: IConfigurationService
99
) {
100
super(
101
Parts.AUXILIARYBAR_PART,
102
{
103
hasTitle: true,
104
trailingSeparator: true,
105
borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0,
106
},
107
AuxiliaryBarPart.activeViewSettingsKey,
108
ActiveAuxiliaryContext.bindTo(contextKeyService),
109
AuxiliaryBarFocusContext.bindTo(contextKeyService),
110
'auxiliarybar',
111
'auxiliarybar',
112
undefined,
113
SIDE_BAR_TITLE_BORDER,
114
ViewContainerLocation.AuxiliaryBar,
115
Extensions.Auxiliary,
116
MenuId.AuxiliaryBarTitle,
117
undefined,
118
notificationService,
119
storageService,
120
contextMenuService,
121
layoutService,
122
keybindingService,
123
hoverService,
124
instantiationService,
125
themeService,
126
viewDescriptorService,
127
contextKeyService,
128
extensionService,
129
menuService,
130
);
131
132
// Track visible view containers for auto-hide
133
this.visibleViewContainersTracker = this._register(instantiationService.createInstance(VisibleViewContainersTracker, ViewContainerLocation.AuxiliaryBar));
134
this._register(this.visibleViewContainersTracker.onDidChange((e) => this.onDidChangeAutoHideViewContainers(e)));
135
136
this.configuration = this.resolveConfiguration();
137
138
this._register(configurationService.onDidChangeConfiguration(e => {
139
if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION)) {
140
this.configuration = this.resolveConfiguration();
141
this.onDidChangeActivityBarLocation();
142
} else if (e.affectsConfiguration('workbench.secondarySideBar.showLabels')) {
143
this.configuration = this.resolveConfiguration();
144
this.updateCompositeBar(true);
145
} else if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_AUTO_HIDE)) {
146
this.onDidChangeActivityBarLocation();
147
}
148
}));
149
}
150
151
private onDidChangeAutoHideViewContainers(e: { before: number; after: number }): void {
152
// Only update if auto-hide is enabled and composite bar would show
153
const autoHide = this.configurationService.getValue<boolean>(LayoutSettings.ACTIVITY_BAR_AUTO_HIDE);
154
if (autoHide && (this.configuration.position === ActivityBarPosition.TOP || this.configuration.position === ActivityBarPosition.BOTTOM)) {
155
const visibleBefore = e.before > 1;
156
const visibleAfter = e.after > 1;
157
if (visibleBefore !== visibleAfter) {
158
this.onDidChangeActivityBarLocation();
159
}
160
}
161
}
162
163
private resolveConfiguration(): IAuxiliaryBarPartConfiguration {
164
const position = this.configurationService.getValue<ActivityBarPosition>(LayoutSettings.ACTIVITY_BAR_LOCATION);
165
166
const canShowLabels = position !== ActivityBarPosition.TOP && position !== ActivityBarPosition.BOTTOM; // use same style as activity bar in this case
167
const showLabels = canShowLabels && this.configurationService.getValue('workbench.secondarySideBar.showLabels') !== false;
168
169
return { position, canShowLabels, showLabels };
170
}
171
172
private onDidChangeActivityBarLocation(): void {
173
this.updateCompositeBar();
174
175
const id = this.getActiveComposite()?.getId();
176
if (id) {
177
this.onTitleAreaUpdate(id);
178
}
179
}
180
181
override updateStyles(): void {
182
super.updateStyles();
183
184
const container = assertReturnsDefined(this.getContainer());
185
container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND) || '';
186
const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder);
187
const isPositionLeft = this.layoutService.getSideBarPosition() === Position.RIGHT;
188
189
container.style.color = this.getColor(SIDE_BAR_FOREGROUND) || '';
190
191
container.style.borderLeftColor = borderColor ?? '';
192
container.style.borderRightColor = borderColor ?? '';
193
194
container.style.borderLeftStyle = borderColor && !isPositionLeft ? 'solid' : 'none';
195
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : 'none';
196
197
container.style.borderLeftWidth = borderColor && !isPositionLeft ? '1px' : '0px';
198
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : '0px';
199
}
200
201
protected getCompositeBarOptions(): IPaneCompositeBarOptions {
202
const $this = this;
203
return {
204
partContainerClass: 'auxiliarybar',
205
pinnedViewContainersKey: AuxiliaryBarPart.pinnedViewsKey,
206
placeholderViewContainersKey: AuxiliaryBarPart.placeholdeViewContainersKey,
207
viewContainersWorkspaceStateKey: AuxiliaryBarPart.viewContainersWorkspaceStateKey,
208
icon: !this.configuration.showLabels,
209
orientation: ActionsOrientation.HORIZONTAL,
210
recomputeSizes: true,
211
activityHoverOptions: {
212
position: () => this.getCompositeBarPosition() === CompositeBarPosition.BOTTOM ? HoverPosition.ABOVE : HoverPosition.BELOW,
213
},
214
fillExtraContextMenuActions: actions => this.fillExtraContextMenuActions(actions),
215
compositeSize: 0,
216
iconSize: 16,
217
// Add 10px spacing if the overflow action is visible to no confuse the user with ... between the toolbars
218
get overflowActionSize() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? 40 : 30; },
219
colors: theme => ({
220
activeBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),
221
inactiveBackgroundColor: theme.getColor(SIDE_BAR_BACKGROUND),
222
get activeBorderBottomColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_ACTIVE_BORDER); },
223
get activeForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_ACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_FOREGROUND); },
224
get inactiveForegroundColor() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_INACTIVE_TITLE_FOREGROUND) : theme.getColor(ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND); },
225
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
226
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
227
get dragAndDropBorder() { return $this.getCompositeBarPosition() === CompositeBarPosition.TITLE ? theme.getColor(PANEL_DRAG_AND_DROP_BORDER) : theme.getColor(ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER); }
228
}),
229
compact: true
230
};
231
}
232
233
private fillExtraContextMenuActions(actions: IAction[]): void {
234
const currentPositionRight = this.layoutService.getSideBarPosition() === Position.LEFT;
235
236
if (this.getCompositeBarPosition() === CompositeBarPosition.TITLE) {
237
const viewsSubmenuAction = this.getViewsSubmenuAction();
238
if (viewsSubmenuAction) {
239
actions.push(new Separator());
240
actions.push(viewsSubmenuAction);
241
}
242
}
243
244
const activityBarPositionMenu = this.menuService.getMenuActions(MenuId.ActivityBarPositionMenu, this.contextKeyService, { shouldForwardArgs: true, renderShortTitle: true });
245
const positionActions = getContextMenuActions(activityBarPositionMenu).secondary;
246
247
const toggleShowLabelsAction = toAction({
248
id: 'workbench.action.auxiliarybar.toggleShowLabels',
249
label: this.configuration.showLabels ? localize('showIcons', "Show Icons") : localize('showLabels', "Show Labels"),
250
enabled: this.configuration.canShowLabels,
251
run: () => this.configurationService.updateValue('workbench.secondarySideBar.showLabels', !this.configuration.showLabels)
252
});
253
254
actions.push(...[
255
new Separator(),
256
new SubmenuAction('workbench.action.panel.position', localize('activity bar position', "Activity Bar Position"), positionActions),
257
toAction({ id: ToggleSidebarPositionAction.ID, label: currentPositionRight ? localize('move second side bar left', "Move Secondary Side Bar Left") : localize('move second side bar right', "Move Secondary Side Bar Right"), run: () => this.commandService.executeCommand(ToggleSidebarPositionAction.ID) }),
258
toggleShowLabelsAction,
259
toAction({ id: ToggleAuxiliaryBarAction.ID, label: localize('hide second side bar', "Hide Secondary Side Bar"), run: () => this.commandService.executeCommand(ToggleAuxiliaryBarAction.ID) })
260
]);
261
}
262
263
protected shouldShowCompositeBar(): boolean {
264
if (this.configuration.position === ActivityBarPosition.HIDDEN) {
265
return false;
266
}
267
268
// Check if auto-hide is enabled and there's only one visible view container
269
// while the activity bar is configured to be top or bottom.
270
if (this.configuration.position === ActivityBarPosition.TOP || this.configuration.position === ActivityBarPosition.BOTTOM) {
271
const autoHide = this.configurationService.getValue<boolean>(LayoutSettings.ACTIVITY_BAR_AUTO_HIDE);
272
if (autoHide) {
273
// Use visible composite count from the composite bar if available (considers pinned state),
274
// otherwise fall back to the tracker's count (based on active view descriptors).
275
// Note: We access paneCompositeBar directly to avoid circular calls with getVisiblePaneCompositeIds()
276
const visibleCount = this.visibleViewContainersTracker.visibleCount;
277
if (visibleCount <= 1) {
278
return false;
279
}
280
}
281
}
282
283
return true;
284
}
285
286
protected getCompositeBarPosition(): CompositeBarPosition {
287
switch (this.configuration.position) {
288
case ActivityBarPosition.TOP: return CompositeBarPosition.TOP;
289
case ActivityBarPosition.BOTTOM: return CompositeBarPosition.BOTTOM;
290
case ActivityBarPosition.HIDDEN: return CompositeBarPosition.TITLE;
291
case ActivityBarPosition.DEFAULT: return CompositeBarPosition.TITLE;
292
default: return CompositeBarPosition.TITLE;
293
}
294
}
295
296
override toJSON(): object {
297
return {
298
type: Parts.AUXILIARYBAR_PART
299
};
300
}
301
}
302
303