Skip to content

Commit

Permalink
Fixes microsoft/monaco-editor#2: Fix scrollbars, context menus, mouse…
Browse files Browse the repository at this point in the history
… operations and content widgets layouting when editor is inside a position:fixed container
  • Loading branch information
alexdima committed Jun 23, 2016
1 parent fa3b092 commit e4a183d
Show file tree
Hide file tree
Showing 19 changed files with 358 additions and 354 deletions.
30 changes: 2 additions & 28 deletions src/vs/base/browser/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1794,31 +1794,6 @@ export class Builder implements IDisposable {
this.destroy();
}

/**
* Gets the coordinates of the element relative to the specified parent.
*/
public getPositionRelativeTo(element: HTMLElement): Box;
public getPositionRelativeTo(element: Builder): Box;
public getPositionRelativeTo(element: any): Box {
if (element instanceof Builder) {
element = (<Builder>element).getHTMLElement();
}

let left = DOM.getRelativeLeft(this.currentElement, element);
let top = DOM.getRelativeTop(this.currentElement, element);

return new Box(top, -1, -1, left);
}

/**
* Gets the absolute coordinates of the element.
*/
public getPosition(): Box {
let position = DOM.getTopLeftOffset(this.currentElement);

return new Box(position.top, -1, -1, position.left);
}

/**
* Gets the size (in pixels) of an element, including the margin.
*/
Expand All @@ -1844,10 +1819,9 @@ export class Builder implements IDisposable {
*/
public getClientArea(): Dimension {

// 0.) Try with DOM getDomNodePosition
// 0.) Try with DOM clientWidth / clientHeight
if (this.currentElement !== document.body) {
let dimensions = DOM.getDomNodePosition(this.currentElement);
return new Dimension(dimensions.width, dimensions.height);
return new Dimension(this.currentElement.clientWidth, this.currentElement.clientHeight);
}

// 1.) Try innerWidth / innerHeight
Expand Down
30 changes: 11 additions & 19 deletions src/vs/base/browser/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,20 +589,23 @@ export function getTopLeftOffset(element: HTMLElement): { left: number; top: num
};
}

export interface IDomNodePosition {
export interface IDomNodePagePosition {
left: number;
top: number;
width: number;
height: number;
}

export function getDomNodePosition(domNode: HTMLElement): IDomNodePosition {
let r = getTopLeftOffset(domNode);
/**
* Returns the position of a dom node relative to the entire page.
*/
export function getDomNodePagePosition(domNode: HTMLElement): IDomNodePagePosition {
let bb = domNode.getBoundingClientRect();
return {
left: r.left,
top: r.top,
width: domNode.clientWidth,
height: domNode.clientHeight
left: bb.left + window.scrollX,
top: bb.top + window.scrollY,
width: bb.width,
height: bb.height
};
}

Expand Down Expand Up @@ -637,7 +640,7 @@ export function getTotalHeight(element: HTMLElement): number {
}

// Gets the left coordinate of the specified element relative to the specified parent.
export function getRelativeLeft(element: HTMLElement, parent: HTMLElement): number {
function getRelativeLeft(element: HTMLElement, parent: HTMLElement): number {
if (element === null) {
return 0;
}
Expand All @@ -647,17 +650,6 @@ export function getRelativeLeft(element: HTMLElement, parent: HTMLElement): numb
return elementPosition.left - parentPosition.left;
}

// Gets the top coordinate of the element relative to the specified parent.
export function getRelativeTop(element: HTMLElement, parent: HTMLElement): number {
if (element === null) {
return 0;
}

let elementPosition = getTopLeftOffset(element);
let parentPosition = getTopLeftOffset(parent);
return parentPosition.top - elementPosition.top;
}

export function getLargestChildWidth(parent: HTMLElement, children: HTMLElement[]): number {
let childWidths = children.map((child) => {
return getTotalWidth(child) + getRelativeLeft(child, parent) || 0;
Expand Down
50 changes: 16 additions & 34 deletions src/vs/base/browser/keyboardEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export function setExtractKeyCode(newExtractKeyCode:(e:KeyboardEvent)=>KeyCode):
}

export interface IKeyboardEvent {
browserEvent:Event;
browserEvent:KeyboardEvent;
target:HTMLElement;

ctrlKey: boolean;
Expand All @@ -180,7 +180,6 @@ export interface IKeyboardEvent {
metaKey: boolean;
keyCode: KeyCode;

clone():IKeyboardEvent;
asKeybinding(): number;
equals(keybinding:number): boolean;

Expand All @@ -206,39 +205,26 @@ export class StandardKeyboardEvent implements IKeyboardEvent {

private _asKeybinding: number;

constructor(source:StandardKeyboardEvent|KeyboardEvent) {
if (source instanceof StandardKeyboardEvent) {
this.browserEvent = null;
this.target = source.target;
constructor(source:KeyboardEvent) {
let e = <KeyboardEvent>source;

this.ctrlKey = source.ctrlKey;
this.shiftKey = source.shiftKey;
this.altKey = source.altKey;
this.metaKey = source.metaKey;
this.keyCode = source.keyCode;
this.browserEvent = e;
this.target = <HTMLElement>e.target;

this._asKeybinding = source._asKeybinding;
} else {
let e = <KeyboardEvent>source;

this.browserEvent = e;
this.target = e.target || (<any>e).targetNode;

this.ctrlKey = e.ctrlKey;
this.shiftKey = e.shiftKey;
this.altKey = e.altKey;
this.metaKey = e.metaKey;
this.keyCode = extractKeyCode(e);
this.ctrlKey = e.ctrlKey;
this.shiftKey = e.shiftKey;
this.altKey = e.altKey;
this.metaKey = e.metaKey;
this.keyCode = extractKeyCode(e);

// console.info(e.type + ": keyCode: " + e.keyCode + ", which: " + e.which + ", charCode: " + e.charCode + ", detail: " + e.detail + " ====> " + this.keyCode + ' -- ' + KeyCode[this.keyCode]);
// console.info(e.type + ": keyCode: " + e.keyCode + ", which: " + e.which + ", charCode: " + e.charCode + ", detail: " + e.detail + " ====> " + this.keyCode + ' -- ' + KeyCode[this.keyCode]);

this.ctrlKey = this.ctrlKey || this.keyCode === KeyCode.Ctrl;
this.altKey = this.altKey || this.keyCode === KeyCode.Alt;
this.shiftKey = this.shiftKey || this.keyCode === KeyCode.Shift;
this.metaKey = this.metaKey || this.keyCode === KeyCode.Meta;
this.ctrlKey = this.ctrlKey || this.keyCode === KeyCode.Ctrl;
this.altKey = this.altKey || this.keyCode === KeyCode.Alt;
this.shiftKey = this.shiftKey || this.keyCode === KeyCode.Shift;
this.metaKey = this.metaKey || this.keyCode === KeyCode.Meta;

this._asKeybinding = this._computeKeybinding();
}
this._asKeybinding = this._computeKeybinding();
}

public preventDefault(): void {
Expand All @@ -253,10 +239,6 @@ export class StandardKeyboardEvent implements IKeyboardEvent {
}
}

public clone(): StandardKeyboardEvent {
return new StandardKeyboardEvent(this);
}

public asKeybinding(): number {
return this._asKeybinding;
}
Expand Down
38 changes: 8 additions & 30 deletions src/vs/base/browser/mouseEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,46 +51,24 @@ export class StandardMouseEvent implements IMouseEvent {
this.middleButton = e.button === 1;
this.rightButton = e.button === 2;

this.target = e.target || (<any>e).targetNode || e.srcElement;
this.target = <HTMLElement>e.target;

this.detail = e.detail || 1;
if (e.type === 'dblclick') {
this.detail = 2;
}
this.posx = 0;
this.posy = 0;
this.ctrlKey = e.ctrlKey;
this.shiftKey = e.shiftKey;
this.altKey = e.altKey;
this.metaKey = e.metaKey;

let readClientCoords = () => {
if (e.clientX || e.clientY) {
this.posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
this.posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
return true;
}
return false;
};

let readPageCoords = () => {
if (e.pageX || e.pageY) {
this.posx = e.pageX;
this.posy = e.pageY;
return true;
}
return false;
};

let test1 = readPageCoords, test2 = readClientCoords;
if (browser.isIE10) {
// The if A elseif B logic here is inversed in IE10 due to an IE10 issue
test1 = readClientCoords;
test2 = readPageCoords;
}

if (!test1()) {
test2();
if (typeof e.pageX === 'number') {
this.posx = e.pageX;
this.posy = e.pageY;
} else {
// Probably hit by MSGestureEvent
this.posx = e.clientX + window.scrollX;
this.posy = e.clientY + window.scrollY;
}

// Find the position of the iframe this code is executing in relative to the iframe where the event was captured.
Expand Down
81 changes: 49 additions & 32 deletions src/vs/base/browser/ui/contextview/contextview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,32 +56,49 @@ export interface ISize {

export interface IView extends IPosition, ISize { }

export function layout(view: ISize, around: IView, inside: IView, anchorPosition: AnchorPosition, anchorAlignment: AnchorAlignment): IPosition {
let top: number, left: number;
function layout(view: ISize, around: IView, viewport: IView, anchorPosition: AnchorPosition, anchorAlignment: AnchorAlignment): IPosition {

if (anchorPosition === AnchorPosition.BELOW) {
top = around.top + around.height - inside.top;
if (inside.top + top + view.height > inside.height && around.top - inside.top > view.height) {
top = around.top - view.height - inside.top;
let chooseBiased = (a:number, aIsGood:boolean, b:number, bIsGood:boolean) => {
if (aIsGood) {
return a;
}
} else {
top = around.top - view.height - inside.top;
if (top + inside.top < 0 && around.top + around.height + view.height - inside.top < inside.height) {
top = around.top + around.height - inside.top;
if (bIsGood) {
return b;
}
}
return a;
};

if (anchorAlignment === AnchorAlignment.LEFT) {
left = around.left - inside.left;
if (inside.left + left + view.width > inside.width) {
left -= view.width - around.width;
}
} else {
left = around.left + around.width - view.width - inside.left;
if (left + inside.left < 0 && around.left + view.width < inside.width) {
left = around.left - inside.left;
let chooseOne = (a:number, aIsGood:boolean, b:number, bIsGood:boolean, aIsPreferred:boolean) => {
if (aIsPreferred) {
return chooseBiased(a, aIsGood, b, bIsGood);
} else {
return chooseBiased(b, bIsGood, a, aIsGood);
}
}
};

let top = (() => {
// Compute both options (putting the segment above and below)
let posAbove = around.top - view.height;
let posBelow = around.top + around.height;

// Check for both options if they are good
let aboveIsGood = (posAbove >= viewport.top && posAbove + view.height <= viewport.top + viewport.height);
let belowIsGood = (posBelow >= viewport.top && posBelow + view.height <= viewport.top + viewport.height);

return chooseOne(posAbove, aboveIsGood, posBelow, belowIsGood, anchorPosition === AnchorPosition.ABOVE);
})();

let left = (() => {
// Compute both options (aligning left and right)
let posLeft = around.left;
let posRight = around.left + around.width - view.width;

// Check for both options if they are good
let leftIsGood = (posLeft >= viewport.left && posLeft + view.width <= viewport.left + viewport.width);
let rightIsGood = (posRight >= viewport.left && posRight + view.width <= viewport.left + viewport.width);

return chooseOne(posLeft, leftIsGood, posRight, rightIsGood, anchorAlignment === AnchorAlignment.LEFT);
})();

return { top: top, left: left };
}
Expand Down Expand Up @@ -173,15 +190,13 @@ export class ContextView extends EventEmitter {

// Get the element's position and size (to anchor the view)
if (DOM.isHTMLElement(anchor)) {
let $anchor = $(<HTMLElement>anchor);
let elementPosition = $anchor.getPosition();
let elementSize = $anchor.getTotalSize();
let elementPosition = DOM.getDomNodePagePosition(anchor);

around = {
top: elementPosition.top,
left: elementPosition.left,
width: elementSize.width,
height: elementSize.height
width: elementPosition.width,
height: elementPosition.height
};
} else {
let realAnchor = <IAnchor>anchor;
Expand All @@ -194,11 +209,9 @@ export class ContextView extends EventEmitter {
};
}

// Get the container's position
let insidePosition = this.$container.getPosition();
let inside = {
top: insidePosition.top,
left: insidePosition.left,
let viewport = {
top: window.scrollY,
left: window.scrollX,
height: window.innerHeight,
width: window.innerWidth
};
Expand All @@ -210,7 +223,11 @@ export class ContextView extends EventEmitter {
let anchorPosition = this.delegate.anchorPosition || AnchorPosition.BELOW;
let anchorAlignment = this.delegate.anchorAlignment || AnchorAlignment.LEFT;

let result = layout(view, around, inside, anchorPosition, anchorAlignment);
let result = layout(view, around, viewport, anchorPosition, anchorAlignment);

let containerPosition = DOM.getDomNodePagePosition(this.$container.getHTMLElement());
result.top -= containerPosition.top;
result.left -= containerPosition.left;

this.$view.removeClass('top', 'bottom', 'left', 'right');
this.$view.addClass(anchorPosition === AnchorPosition.BELOW ? 'bottom' : 'top');
Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export abstract class AbstractScrollbar extends Widget {
}

private _onMouseDown(e: IMouseEvent): void {
let domNodePosition = DomUtils.getDomNodePosition(this.domNode.domNode);
let domNodePosition = DomUtils.getDomNodePagePosition(this.domNode.domNode);
let desiredSliderPosition = this._mouseDownRelativePosition(e, domNodePosition) - this._scrollbarState.getArrowSize() - this._scrollbarState.getSliderSize() / 2;
this.setDesiredScrollPosition(this._scrollbarState.convertSliderPositionToScrollPosition(desiredSliderPosition));
this._sliderMouseDown(e);
Expand Down Expand Up @@ -255,7 +255,7 @@ export abstract class AbstractScrollbar extends Widget {

protected abstract _renderDomNode(largeSize: number, smallSize: number): void;
protected abstract _updateSlider(sliderSize: number, sliderPosition: number): void;
protected abstract _mouseDownRelativePosition(e: IMouseEvent, domNodePosition: DomUtils.IDomNodePosition): number;
protected abstract _mouseDownRelativePosition(e: IMouseEvent, domNodePosition: DomUtils.IDomNodePagePosition): number;
protected abstract _sliderMousePosition(e: IMouseMoveEventData): number;
protected abstract _sliderOrthogonalMousePosition(e: IMouseMoveEventData): number;
protected abstract _getScrollPosition(): number;
Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import {AbstractScrollbar, ScrollbarHost, IMouseMoveEventData} from 'vs/base/browser/ui/scrollbar/abstractScrollbar';
import {IMouseEvent, StandardMouseWheelEvent} from 'vs/base/browser/mouseEvent';
import {IDomNodePosition} from 'vs/base/browser/dom';
import {IDomNodePagePosition} from 'vs/base/browser/dom';
import {ScrollbarVisibility, ScrollableElementResolvedOptions} from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
import {Scrollable, ScrollEvent} from 'vs/base/common/scrollable';
import {ScrollbarState} from 'vs/base/browser/ui/scrollbar/scrollbarState';
Expand Down Expand Up @@ -84,7 +84,7 @@ export class HorizontalScrollbar extends AbstractScrollbar {
return this._shouldRender;
}

protected _mouseDownRelativePosition(e: IMouseEvent, domNodePosition: IDomNodePosition): number {
protected _mouseDownRelativePosition(e: IMouseEvent, domNodePosition: IDomNodePagePosition): number {
return e.posx - domNodePosition.left;
}

Expand Down
Loading

0 comments on commit e4a183d

Please sign in to comment.