Skip to content

Commit ee1fb7b

Browse files
committed
1 parent 1bcb101 commit ee1fb7b

2 files changed

Lines changed: 75 additions & 54 deletions

File tree

src/vs/base/browser/ui/contextview/contextview.ts

Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -54,51 +54,46 @@ export interface ISize {
5454

5555
export interface IView extends IPosition, ISize { }
5656

57-
function layout(view: ISize, around: IView, viewport: IView, anchorPosition: AnchorPosition, anchorAlignment: AnchorAlignment): IPosition {
57+
export enum LayoutAnchorPosition {
58+
Before,
59+
After
60+
}
5861

59-
let chooseBiased = (a: number, aIsGood: boolean, b: number, bIsGood: boolean) => {
60-
if (aIsGood) {
61-
return a;
62-
}
63-
if (bIsGood) {
64-
return b;
65-
}
66-
return a;
67-
};
62+
export interface ILayoutAnchor {
63+
offset: number;
64+
size: number;
65+
position: LayoutAnchorPosition;
66+
}
6867

69-
let chooseOne = (a: number, aIsGood: boolean, b: number, bIsGood: boolean, aIsPreferred: boolean) => {
70-
if (aIsPreferred) {
71-
return chooseBiased(a, aIsGood, b, bIsGood);
72-
} else {
73-
return chooseBiased(b, bIsGood, a, aIsGood);
68+
/**
69+
* Lays out a one dimensional view next to an anchor in a viewport.
70+
*
71+
* @returns The view offset within the viewport.
72+
*/
73+
export function layout(viewportSize: number, viewSize: number, anchor: ILayoutAnchor): number {
74+
const anchorEnd = anchor.offset + anchor.size;
75+
76+
if (anchor.position === LayoutAnchorPosition.Before) {
77+
if (viewSize <= viewportSize - anchorEnd) {
78+
return anchorEnd; // happy case, lay it out after the anchor
7479
}
75-
};
76-
77-
let top = (() => {
78-
// Compute both options (putting the segment above and below)
79-
let posAbove = around.top - view.height;
80-
let posBelow = around.top + around.height;
81-
82-
// Check for both options if they are good
83-
let aboveIsGood = (posAbove >= viewport.top && posAbove + view.height <= viewport.top + viewport.height);
84-
let belowIsGood = (posBelow >= viewport.top && posBelow + view.height <= viewport.top + viewport.height);
8580

86-
return chooseOne(posAbove, aboveIsGood, posBelow, belowIsGood, anchorPosition === AnchorPosition.ABOVE);
87-
})();
88-
89-
let left = (() => {
90-
// Compute both options (aligning left and right)
91-
let posLeft = around.left;
92-
let posRight = around.left + around.width - view.width;
81+
if (viewSize <= anchor.offset) {
82+
return anchor.offset - viewSize; // ok case, lay it out before the anchor
83+
}
9384

94-
// Check for both options if they are good
95-
let leftIsGood = (posLeft >= viewport.left && posLeft + view.width <= viewport.left + viewport.width);
96-
let rightIsGood = (posRight >= viewport.left && posRight + view.width <= viewport.left + viewport.width);
85+
return Math.max(viewportSize - viewSize, 0); // sad case, lay it over the anchor
86+
} else {
87+
if (viewSize <= anchor.offset) {
88+
return anchor.offset - viewSize; // happy case, lay it out before the anchor
89+
}
9790

98-
return chooseOne(posLeft, leftIsGood, posRight, rightIsGood, anchorAlignment === AnchorAlignment.LEFT);
99-
})();
91+
if (viewSize <= viewportSize - anchorEnd) {
92+
return anchorEnd; // ok case, lay it out after the anchor
93+
}
10094

101-
return { top: top, left: left };
95+
return 0; // sad case, lay it over the anchor
96+
}
10297
}
10398

10499
export class ContextView {
@@ -205,30 +200,28 @@ export class ContextView {
205200
};
206201
}
207202

208-
let viewport = {
209-
top: DOM.StandardWindow.scrollY,
210-
left: DOM.StandardWindow.scrollX,
211-
height: window.innerHeight,
212-
width: window.innerWidth
213-
};
203+
const viewSize = this.$view.getTotalSize();
204+
const anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW;
205+
const anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT;
214206

215-
// Get the view's size
216-
let viewSize = this.$view.getTotalSize();
217-
let view = { width: viewSize.width, height: viewSize.height };
207+
const verticalAnchor: ILayoutAnchor = { offset: around.top, size: around.height, position: anchorPosition === AnchorPosition.BELOW ? LayoutAnchorPosition.Before : LayoutAnchorPosition.After };
218208

219-
let anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW;
220-
let anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT;
209+
let horizontalAnchor: ILayoutAnchor;
221210

222-
let result = layout(view, around, viewport, anchorPosition, anchorAlignment);
211+
if (anchorAlignment === AnchorAlignment.LEFT) {
212+
horizontalAnchor = { offset: around.left, size: 0, position: LayoutAnchorPosition.Before };
213+
} else {
214+
horizontalAnchor = { offset: around.left + around.width, size: 0, position: LayoutAnchorPosition.After };
215+
}
223216

224-
let containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement());
225-
result.top -= containerPosition.top;
226-
result.left -= containerPosition.left;
217+
const containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement());
218+
const top = layout(window.innerHeight, viewSize.height, verticalAnchor) - containerPosition.top;
219+
const left = layout(window.innerWidth, viewSize.width, horizontalAnchor) - containerPosition.left;
227220

228221
this.$view.removeClass('top', 'bottom', 'left', 'right');
229222
this.$view.addClass(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
230223
this.$view.addClass(anchorAlignment === AnchorAlignment.LEFT ? 'left' : 'right');
231-
this.$view.style({ top: result.top + 'px', left: result.left + 'px', width: 'initial' });
224+
this.$view.style({ top: `${top}px`, left: `${left}px`, width: 'initial' });
232225
}
233226

234227
public hide(data?: any): void {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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 * as assert from 'assert';
7+
import { layout, LayoutAnchorPosition } from 'vs/base/browser/ui/contextview/contextview';
8+
9+
suite('Contextview', function () {
10+
11+
test('layout', function () {
12+
assert.equal(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.Before }), 0);
13+
assert.equal(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.Before }), 50);
14+
assert.equal(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.Before }), 180);
15+
16+
assert.equal(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.After }), 0);
17+
assert.equal(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.After }), 30);
18+
assert.equal(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.After }), 180);
19+
20+
assert.equal(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.Before }), 50);
21+
assert.equal(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.Before }), 100);
22+
assert.equal(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.Before }), 130);
23+
24+
assert.equal(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.After }), 50);
25+
assert.equal(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.After }), 30);
26+
assert.equal(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.After }), 130);
27+
});
28+
});

0 commit comments

Comments
 (0)