Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/actions/navigationActions.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 { localize2 } from '../../../nls.js';
7
import { IEditorGroupsService, GroupDirection, GroupLocation, IFindGroupScope } from '../../services/editor/common/editorGroupsService.js';
8
import { IWorkbenchLayoutService, Parts } from '../../services/layout/browser/layoutService.js';
9
import { Action2, IAction2Options, registerAction2 } from '../../../platform/actions/common/actions.js';
10
import { Categories } from '../../../platform/action/common/actionCommonCategories.js';
11
import { Direction } from '../../../base/browser/ui/grid/grid.js';
12
import { KeyCode, KeyMod } from '../../../base/common/keyCodes.js';
13
import { IEditorService } from '../../services/editor/common/editorService.js';
14
import { IPaneComposite } from '../../common/panecomposite.js';
15
import { IComposite } from '../../common/composite.js';
16
import { IPaneCompositePartService } from '../../services/panecomposite/browser/panecomposite.js';
17
import { ViewContainerLocation } from '../../common/views.js';
18
import { KeybindingWeight } from '../../../platform/keybinding/common/keybindingsRegistry.js';
19
import { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js';
20
import { getActiveWindow } from '../../../base/browser/dom.js';
21
import { isAuxiliaryWindow } from '../../../base/browser/window.js';
22
23
abstract class BaseNavigationAction extends Action2 {
24
25
constructor(
26
options: IAction2Options,
27
protected direction: Direction
28
) {
29
super(options);
30
}
31
32
run(accessor: ServicesAccessor): void {
33
const layoutService = accessor.get(IWorkbenchLayoutService);
34
const editorGroupService = accessor.get(IEditorGroupsService);
35
const paneCompositeService = accessor.get(IPaneCompositePartService);
36
37
const isEditorFocus = layoutService.hasFocus(Parts.EDITOR_PART);
38
const isPanelFocus = layoutService.hasFocus(Parts.PANEL_PART);
39
const isSidebarFocus = layoutService.hasFocus(Parts.SIDEBAR_PART);
40
const isAuxiliaryBarFocus = layoutService.hasFocus(Parts.AUXILIARYBAR_PART);
41
42
let neighborPart: Parts | undefined;
43
if (isEditorFocus) {
44
const didNavigate = this.navigateAcrossEditorGroup(this.toGroupDirection(this.direction), editorGroupService);
45
if (didNavigate) {
46
return;
47
}
48
49
neighborPart = layoutService.getVisibleNeighborPart(Parts.EDITOR_PART, this.direction);
50
}
51
52
if (isPanelFocus) {
53
neighborPart = layoutService.getVisibleNeighborPart(Parts.PANEL_PART, this.direction);
54
}
55
56
if (isSidebarFocus) {
57
neighborPart = layoutService.getVisibleNeighborPart(Parts.SIDEBAR_PART, this.direction);
58
}
59
60
if (isAuxiliaryBarFocus) {
61
neighborPart = neighborPart = layoutService.getVisibleNeighborPart(Parts.AUXILIARYBAR_PART, this.direction);
62
}
63
64
if (neighborPart === Parts.EDITOR_PART) {
65
if (!this.navigateBackToEditorGroup(this.toGroupDirection(this.direction), editorGroupService)) {
66
this.navigateToEditorGroup(this.direction === Direction.Right ? GroupLocation.FIRST : GroupLocation.LAST, editorGroupService);
67
}
68
} else if (neighborPart === Parts.SIDEBAR_PART) {
69
this.navigateToSidebar(layoutService, paneCompositeService);
70
} else if (neighborPart === Parts.PANEL_PART) {
71
this.navigateToPanel(layoutService, paneCompositeService);
72
} else if (neighborPart === Parts.AUXILIARYBAR_PART) {
73
this.navigateToAuxiliaryBar(layoutService, paneCompositeService);
74
}
75
}
76
77
private async navigateToPanel(layoutService: IWorkbenchLayoutService, paneCompositeService: IPaneCompositePartService): Promise<IComposite | boolean> {
78
if (!layoutService.isVisible(Parts.PANEL_PART)) {
79
return false;
80
}
81
82
const activePanel = paneCompositeService.getActivePaneComposite(ViewContainerLocation.Panel);
83
if (!activePanel) {
84
return false;
85
}
86
87
const activePanelId = activePanel.getId();
88
89
const res = await paneCompositeService.openPaneComposite(activePanelId, ViewContainerLocation.Panel, true);
90
if (!res) {
91
return false;
92
}
93
94
return res;
95
}
96
97
private async navigateToSidebar(layoutService: IWorkbenchLayoutService, paneCompositeService: IPaneCompositePartService): Promise<IPaneComposite | boolean> {
98
if (!layoutService.isVisible(Parts.SIDEBAR_PART)) {
99
return false;
100
}
101
102
const activeViewlet = paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar);
103
if (!activeViewlet) {
104
return false;
105
}
106
const activeViewletId = activeViewlet.getId();
107
108
const viewlet = await paneCompositeService.openPaneComposite(activeViewletId, ViewContainerLocation.Sidebar, true);
109
return !!viewlet;
110
}
111
112
private async navigateToAuxiliaryBar(layoutService: IWorkbenchLayoutService, paneCompositeService: IPaneCompositePartService): Promise<IComposite | boolean> {
113
if (!layoutService.isVisible(Parts.AUXILIARYBAR_PART)) {
114
return false;
115
}
116
117
const activePanel = paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar);
118
if (!activePanel) {
119
return false;
120
}
121
122
const activePanelId = activePanel.getId();
123
124
const res = await paneCompositeService.openPaneComposite(activePanelId, ViewContainerLocation.AuxiliaryBar, true);
125
if (!res) {
126
return false;
127
}
128
129
return res;
130
}
131
132
private navigateAcrossEditorGroup(direction: GroupDirection, editorGroupService: IEditorGroupsService): boolean {
133
return this.doNavigateToEditorGroup({ direction }, editorGroupService);
134
}
135
136
private navigateToEditorGroup(location: GroupLocation, editorGroupService: IEditorGroupsService): boolean {
137
return this.doNavigateToEditorGroup({ location }, editorGroupService);
138
}
139
140
private navigateBackToEditorGroup(direction: GroupDirection, editorGroupService: IEditorGroupsService): boolean {
141
if (!editorGroupService.activeGroup) {
142
return false;
143
}
144
145
const oppositeDirection = this.toOppositeDirection(direction);
146
147
// Check to see if there is a group in between the last
148
// active group and the direction of movement
149
150
const groupInBetween = editorGroupService.findGroup({ direction: oppositeDirection }, editorGroupService.activeGroup);
151
if (!groupInBetween) {
152
153
// No group in between means we can return
154
// focus to the last active editor group
155
156
editorGroupService.activeGroup.focus();
157
return true;
158
}
159
160
return false;
161
}
162
163
private toGroupDirection(direction: Direction): GroupDirection {
164
switch (direction) {
165
case Direction.Down: return GroupDirection.DOWN;
166
case Direction.Left: return GroupDirection.LEFT;
167
case Direction.Right: return GroupDirection.RIGHT;
168
case Direction.Up: return GroupDirection.UP;
169
}
170
}
171
172
private toOppositeDirection(direction: GroupDirection): GroupDirection {
173
switch (direction) {
174
case GroupDirection.UP: return GroupDirection.DOWN;
175
case GroupDirection.RIGHT: return GroupDirection.LEFT;
176
case GroupDirection.LEFT: return GroupDirection.RIGHT;
177
case GroupDirection.DOWN: return GroupDirection.UP;
178
}
179
}
180
181
private doNavigateToEditorGroup(scope: IFindGroupScope, editorGroupService: IEditorGroupsService): boolean {
182
const targetGroup = editorGroupService.findGroup(scope, editorGroupService.activeGroup);
183
if (targetGroup) {
184
targetGroup.focus();
185
186
return true;
187
}
188
189
return false;
190
}
191
}
192
193
registerAction2(class extends BaseNavigationAction {
194
195
constructor() {
196
super({
197
id: 'workbench.action.navigateLeft',
198
title: localize2('navigateLeft', 'Navigate to the View on the Left'),
199
category: Categories.View,
200
f1: true
201
}, Direction.Left);
202
}
203
});
204
205
registerAction2(class extends BaseNavigationAction {
206
207
constructor() {
208
super({
209
id: 'workbench.action.navigateRight',
210
title: localize2('navigateRight', 'Navigate to the View on the Right'),
211
category: Categories.View,
212
f1: true
213
}, Direction.Right);
214
}
215
});
216
217
registerAction2(class extends BaseNavigationAction {
218
219
constructor() {
220
super({
221
id: 'workbench.action.navigateUp',
222
title: localize2('navigateUp', 'Navigate to the View Above'),
223
category: Categories.View,
224
f1: true
225
}, Direction.Up);
226
}
227
});
228
229
registerAction2(class extends BaseNavigationAction {
230
231
constructor() {
232
super({
233
id: 'workbench.action.navigateDown',
234
title: localize2('navigateDown', 'Navigate to the View Below'),
235
category: Categories.View,
236
f1: true
237
}, Direction.Down);
238
}
239
});
240
241
abstract class BaseFocusAction extends Action2 {
242
243
constructor(
244
options: IAction2Options,
245
private readonly focusNext: boolean
246
) {
247
super(options);
248
}
249
250
run(accessor: ServicesAccessor): void {
251
const layoutService = accessor.get(IWorkbenchLayoutService);
252
const editorService = accessor.get(IEditorService);
253
254
this.focusNextOrPreviousPart(layoutService, editorService, this.focusNext);
255
}
256
257
private findVisibleNeighbour(layoutService: IWorkbenchLayoutService, part: Parts, next: boolean): Parts {
258
const activeWindow = getActiveWindow();
259
const windowIsAuxiliary = isAuxiliaryWindow(activeWindow);
260
261
let neighbour: Parts;
262
if (windowIsAuxiliary) {
263
switch (part) {
264
case Parts.EDITOR_PART:
265
neighbour = Parts.STATUSBAR_PART;
266
break;
267
default:
268
neighbour = Parts.EDITOR_PART;
269
}
270
} else {
271
switch (part) {
272
case Parts.EDITOR_PART:
273
neighbour = next ? Parts.PANEL_PART : Parts.SIDEBAR_PART;
274
break;
275
case Parts.PANEL_PART:
276
neighbour = next ? Parts.AUXILIARYBAR_PART : Parts.EDITOR_PART;
277
break;
278
case Parts.AUXILIARYBAR_PART:
279
neighbour = next ? Parts.STATUSBAR_PART : Parts.PANEL_PART;
280
break;
281
case Parts.STATUSBAR_PART:
282
neighbour = next ? Parts.ACTIVITYBAR_PART : Parts.AUXILIARYBAR_PART;
283
break;
284
case Parts.ACTIVITYBAR_PART:
285
neighbour = next ? Parts.SIDEBAR_PART : Parts.STATUSBAR_PART;
286
break;
287
case Parts.SIDEBAR_PART:
288
neighbour = next ? Parts.EDITOR_PART : Parts.ACTIVITYBAR_PART;
289
break;
290
default:
291
neighbour = Parts.EDITOR_PART;
292
}
293
}
294
295
if (layoutService.isVisible(neighbour, activeWindow) || neighbour === Parts.EDITOR_PART) {
296
return neighbour;
297
}
298
299
return this.findVisibleNeighbour(layoutService, neighbour, next);
300
}
301
302
private focusNextOrPreviousPart(layoutService: IWorkbenchLayoutService, editorService: IEditorService, next: boolean): void {
303
let currentlyFocusedPart: Parts | undefined;
304
if (editorService.activeEditorPane?.hasFocus() || layoutService.hasFocus(Parts.EDITOR_PART)) {
305
currentlyFocusedPart = Parts.EDITOR_PART;
306
} else if (layoutService.hasFocus(Parts.ACTIVITYBAR_PART)) {
307
currentlyFocusedPart = Parts.ACTIVITYBAR_PART;
308
} else if (layoutService.hasFocus(Parts.STATUSBAR_PART)) {
309
currentlyFocusedPart = Parts.STATUSBAR_PART;
310
} else if (layoutService.hasFocus(Parts.SIDEBAR_PART)) {
311
currentlyFocusedPart = Parts.SIDEBAR_PART;
312
} else if (layoutService.hasFocus(Parts.AUXILIARYBAR_PART)) {
313
currentlyFocusedPart = Parts.AUXILIARYBAR_PART;
314
} else if (layoutService.hasFocus(Parts.PANEL_PART)) {
315
currentlyFocusedPart = Parts.PANEL_PART;
316
}
317
318
layoutService.focusPart(currentlyFocusedPart ? this.findVisibleNeighbour(layoutService, currentlyFocusedPart, next) : Parts.EDITOR_PART, getActiveWindow());
319
}
320
}
321
322
registerAction2(class extends BaseFocusAction {
323
324
constructor() {
325
super({
326
id: 'workbench.action.focusNextPart',
327
title: localize2('focusNextPart', 'Focus Next Part'),
328
category: Categories.View,
329
f1: true,
330
keybinding: {
331
primary: KeyCode.F6,
332
weight: KeybindingWeight.WorkbenchContrib
333
}
334
}, true);
335
}
336
});
337
338
registerAction2(class extends BaseFocusAction {
339
340
constructor() {
341
super({
342
id: 'workbench.action.focusPreviousPart',
343
title: localize2('focusPreviousPart', 'Focus Previous Part'),
344
category: Categories.View,
345
f1: true,
346
keybinding: {
347
primary: KeyMod.Shift | KeyCode.F6,
348
weight: KeybindingWeight.WorkbenchContrib
349
}
350
}, false);
351
}
352
});
353
354