1
>
/*---------------------------------------------------------------------------------------------
layout.ts
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
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
}
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;