src/vs/base/common/layout.ts

166 LOC · 103 covered · 63 uncovered · 3 ranges · 1 concepts · 1 introducers · 1 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- range.ts ×5
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 { Range } from './range.js';
7 >
8 > export interface IAnchor {
9 > x: number;
10 > y: number;
11 > width?: number;
12 > height?: number;
13 > }
14 >
15 > export const enum AnchorAlignment {
16 > LEFT, RIGHT
17 > }
18 >
19 > export const enum AnchorPosition {
20 > BELOW, ABOVE
21 > }
22 >
23 > export const enum AnchorAxisAlignment {
24 > VERTICAL, HORIZONTAL
25 > }
26 >
27 > interface IPosition {
28 > readonly top: number;
29 > readonly left: number;
30 > }
31 >
32 > interface ISize {
33 > readonly width: number;
34 > readonly height: number;
35 > }
36 >
37 > export interface IRect extends IPosition, ISize { }
38 >
39 > export const enum LayoutAnchorPosition {
40 > Before,
41 > After
42 > }
43 >
44 > export enum LayoutAnchorMode {
45 > AVOID,
46 > ALIGN
47 > }
48 >
49 > export interface ILayoutAnchor {
50 > offset: number;
51 > size: number;
52 > mode?: LayoutAnchorMode; // default: AVOID
53 > position: LayoutAnchorPosition;
54 > }
55 >
56 > export interface ILayoutResult {
57 > position: number;
58 > result: 'ok' | 'flipped' | 'overlap';
59 > }
60 >
61 > /**
62 > * Lays out a one dimensional view next to an anchor in a viewport.
63 > *
64 > * @returns The view offset within the viewport.
65 > */
66 > export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAnchor): ILayoutResult {
67 > const layoutAfterAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset : anchor.offset + anchor.size;
68 > const layoutBeforeAnchorBoundary = anchor.mode === LayoutAnchorMode.ALIGN ? anchor.offset + anchor.size : anchor.offset;
69 >
70 > if (anchor.position === LayoutAnchorPosition.Before) {
71 > if (viewSize <= viewportSize - layoutAfterAnchorBoundary) {
72 > return { position: layoutAfterAnchorBoundary, result: 'ok' }; // happy case, lay it out after the anchor
73 > }
74 >
75 > if (viewSize <= layoutBeforeAnchorBoundary) {
76 > return { position: layoutBeforeAnchorBoundary - viewSize, result: 'flipped' }; // ok case, lay it out before the anchor
77 > }
78
79 return { position: Math.max(viewportSize - viewSize, 0), result: 'overlap' }; // sad case, lay it over the anchor
80 > } else { range.ts ×5
81 > if (viewSize <= layoutBeforeAnchorBoundary) {
82 > return { position: layoutBeforeAnchorBoundary - viewSize, result: 'ok' }; // happy case, lay it out before the anchor
83 > }
84 >
85 > if (viewSize <= viewportSize - layoutAfterAnchorBoundary && layoutBeforeAnchorBoundary < viewSize / 2) {
86 > return { position: layoutAfterAnchorBoundary, result: 'flipped' }; // ok case, lay it out after the anchor
87 > }
88
89 return { position: 0, result: 'overlap' }; // sad case, lay it over the anchor
90 }
91 > } range.ts ×5
92 >
93 > interface ILayout2DOptions {
94 > readonly anchorAlignment?: AnchorAlignment; // default: left
95 > readonly anchorPosition?: AnchorPosition; // default: above
96 > readonly anchorAxisAlignment?: AnchorAxisAlignment; // default: vertical
97 > }
98 >
99 > export interface ILayout2DResult {
100 > top: number;
101 > left: number;
102 > bottom: number;
103 > right: number;
104 > anchorAlignment: AnchorAlignment;
105 > anchorPosition: AnchorPosition;
106 > }
107 >
108 > export function layout2d(viewport: IRect, view: ISize, anchor: IRect, options?: ILayout2DOptions): ILayout2DResult {
109 let anchorAlignment = options?.anchorAlignment ?? AnchorAlignment.LEFT;
110 let anchorPosition = options?.anchorPosition ?? AnchorPosition.BELOW;
111 const anchorAxisAlignment = options?.anchorAxisAlignment ?? AnchorAxisAlignment.VERTICAL;
112
113 let top: number;
114 let left: number;
115
116 if (anchorAxisAlignment === AnchorAxisAlignment.VERTICAL) {
117 const verticalAnchor: ILayoutAnchor = { offset: anchor.top - viewport.top, size: anchor.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
118 const horizontalAnchor: ILayoutAnchor = { offset: anchor.left, size: anchor.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN };
119
120 const verticalLayoutResult = layout(viewport.height, view.height, verticalAnchor);
121 top = verticalLayoutResult.position + viewport.top;
122
123 if (verticalLayoutResult.result === 'flipped') {
124 anchorPosition = anchorPosition === AnchorPosition.BELOW ? AnchorPosition.ABOVE : AnchorPosition.BELOW;
125 }
126
127 // if view intersects vertically with anchor, we must avoid the anchor
128 if (Range.intersects({ start: top, end: top + view.height }, { start: verticalAnchor.offset, end: verticalAnchor.offset + verticalAnchor.size })) {
129 horizontalAnchor.mode = LayoutAnchorMode.AVOID;
130 }
131
132 const horizontalLayoutResult = layout(viewport.width, view.width, horizontalAnchor);
133 left = horizontalLayoutResult.position;
134
135 if (horizontalLayoutResult.result === 'flipped') {
136 anchorAlignment = anchorAlignment === AnchorAlignment.LEFT ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT;
137 }
138 } else {
139 const horizontalAnchor: ILayoutAnchor = { offset: anchor.left, size: anchor.width, position: anchorAlignment === AnchorAlignment.LEFT ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
140 const verticalAnchor: ILayoutAnchor = { offset: anchor.top, size: anchor.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After, mode: LayoutAnchorMode.ALIGN };
141
142 const horizontalLayoutResult = layout(viewport.width, view.width, horizontalAnchor);
143 left = horizontalLayoutResult.position;
144
145 if (horizontalLayoutResult.result === 'flipped') {
146 anchorAlignment = anchorAlignment === AnchorAlignment.LEFT ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT;
147 }
148
149 // if view intersects horizontally with anchor, we must avoid the anchor
150 if (Range.intersects({ start: left, end: left + view.width }, { start: horizontalAnchor.offset, end: horizontalAnchor.offset + horizontalAnchor.size })) {
151 verticalAnchor.mode = LayoutAnchorMode.AVOID;
152 }
153
154 const verticalLayoutResult = layout(viewport.height, view.height, verticalAnchor);
155 top = verticalLayoutResult.position + viewport.top;
156
157 if (verticalLayoutResult.result === 'flipped') {
158 anchorPosition = anchorPosition === AnchorPosition.BELOW ? AnchorPosition.ABOVE : AnchorPosition.BELOW;
159 }
160 }
161
162 const right = viewport.width - (left + view.width);
163 const bottom = viewport.height - (top + view.height);
164
165 return { top, left, bottom, right, anchorAlignment, anchorPosition };
166 }