improve hover position logic
This commit is contained in:
parent
b6ff982121
commit
7d13ce88e7
2 changed files with 175 additions and 69 deletions
|
@ -21,7 +21,7 @@ import { CompositeDragAndDropObserver, ICompositeDragAndDrop, Before2D, toggleDr
|
|||
import { Color } from 'vs/base/common/color';
|
||||
import { IBaseActionViewItemOptions, BaseActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
import { Codicon } from 'vs/base/common/codicons';
|
||||
import { IHoverService, IHoverTarget } from 'vs/workbench/services/hover/browser/hover';
|
||||
import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
@ -415,15 +415,9 @@ export class ActivityActionViewItem extends BaseActionViewItem {
|
|||
if (this.hover.value) {
|
||||
return;
|
||||
}
|
||||
const { bottom } = this.container.getBoundingClientRect();
|
||||
const hoverPosition = this.options.hoverOptions!.position();
|
||||
const target: IHoverTarget | HTMLElement = hoverPosition === HoverPosition.LEFT || hoverPosition === HoverPosition.RIGHT ? {
|
||||
targetElements: [this.container],
|
||||
y: bottom - 10,
|
||||
dispose: () => { }
|
||||
} : this.container;
|
||||
this.hover.value = this.hoverService.showHover({
|
||||
target,
|
||||
target: this.container,
|
||||
hoverPosition,
|
||||
text: this.computeTitle(),
|
||||
showPointer: true,
|
||||
|
|
|
@ -20,6 +20,15 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer';
|
||||
|
||||
const $ = dom.$;
|
||||
type TargetRect = {
|
||||
left: number,
|
||||
right: number,
|
||||
top: number,
|
||||
bottom: number,
|
||||
width: number,
|
||||
height: number,
|
||||
center: { x: number, y: number },
|
||||
};
|
||||
|
||||
export class HoverWidget extends Widget {
|
||||
private readonly _messageListeners = new DisposableStore();
|
||||
|
@ -170,82 +179,185 @@ export class HoverWidget extends Widget {
|
|||
this._hover.contentsDomNode.style.maxHeight = '';
|
||||
|
||||
const targetBounds = this._target.targetElements.map(e => e.getBoundingClientRect());
|
||||
const top = Math.min(...targetBounds.map(e => e.top));
|
||||
const right = Math.max(...targetBounds.map(e => e.right));
|
||||
const bottom = Math.max(...targetBounds.map(e => e.bottom));
|
||||
const left = Math.min(...targetBounds.map(e => e.left));
|
||||
const width = right - left;
|
||||
const height = bottom - top;
|
||||
|
||||
// Get horizontal alignment and position
|
||||
if (this._target.x !== undefined) {
|
||||
this._x = this._target.x;
|
||||
} else if (this._hoverPosition === HoverPosition.RIGHT) {
|
||||
this._x = Math.max(...targetBounds.map(e => e.right));
|
||||
} else if (this._hoverPosition === HoverPosition.LEFT) {
|
||||
this._x = Math.min(...targetBounds.map(e => e.left)) - this._hover.containerDomNode.clientWidth - 1;
|
||||
} else {
|
||||
const targetLeft = Math.min(...targetBounds.map(e => e.left));
|
||||
if (targetLeft + this._hover.containerDomNode.clientWidth >= document.documentElement.clientWidth) {
|
||||
this._x = document.documentElement.clientWidth - this._workbenchLayoutService.getWindowBorderWidth() - 1;
|
||||
this._hover.containerDomNode.classList.add('right-aligned');
|
||||
} else {
|
||||
this._x = targetLeft;
|
||||
const targetRect: TargetRect = {
|
||||
top, right, bottom, left, width, height,
|
||||
center: {
|
||||
x: left + (width / 2),
|
||||
y: top + (height / 2)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Get vertical alignment and position
|
||||
if (this._target.y !== undefined) {
|
||||
this._y = this._target.y;
|
||||
} else if (this._hoverPosition === HoverPosition.RIGHT || this._hoverPosition === HoverPosition.LEFT) {
|
||||
this._y = Math.max(...targetBounds.map(e => e.bottom));
|
||||
} else if (this._hoverPosition === HoverPosition.ABOVE) {
|
||||
const targetTop = Math.min(...targetBounds.map(e => e.top));
|
||||
if (targetTop - this._hover.containerDomNode.clientHeight < 0) {
|
||||
const targetBottom = Math.max(...targetBounds.map(e => e.bottom));
|
||||
this._hoverPosition = HoverPosition.BELOW;
|
||||
this._y = targetBottom - 2;
|
||||
} else {
|
||||
this._y = targetTop;
|
||||
}
|
||||
} else {
|
||||
const targetBottom = Math.max(...targetBounds.map(e => e.bottom));
|
||||
if (targetBottom + this._hover.containerDomNode.clientHeight > window.innerHeight) {
|
||||
const targetTop = Math.min(...targetBounds.map(e => e.top));
|
||||
this._hoverPosition = HoverPosition.ABOVE;
|
||||
this._y = targetTop;
|
||||
} else {
|
||||
this._y = targetBottom - 2;
|
||||
}
|
||||
}
|
||||
this.adjustHorizontalHoverPosition(targetRect);
|
||||
this.adjustVerticalHoverPosition(targetRect);
|
||||
this.computeXCordinate(targetRect);
|
||||
this.computeYCordinate(targetRect);
|
||||
|
||||
if (this._hoverPointer) {
|
||||
|
||||
// reset
|
||||
this._hoverPointer.classList.remove('top');
|
||||
this._hoverPointer.classList.remove('left');
|
||||
this._hoverPointer.classList.remove('right');
|
||||
this._hoverPointer.classList.remove('bottom');
|
||||
|
||||
if (this._hoverPosition === HoverPosition.RIGHT) {
|
||||
this._hoverPointer.classList.add('left');
|
||||
this._hoverPointer.style.top = `${(this._hover.containerDomNode.clientHeight / 2) - 3}px`;
|
||||
}
|
||||
|
||||
else if (this._hoverPosition === HoverPosition.LEFT) {
|
||||
this._hoverPointer.classList.add('right');
|
||||
this._hoverPointer.style.top = `${(this._hover.containerDomNode.clientHeight / 2) - 3}px`;
|
||||
}
|
||||
|
||||
else if (this._hoverPosition === HoverPosition.BELOW) {
|
||||
this._hoverPointer.classList.add('top');
|
||||
this._hoverPointer.style.left = `${(this._hover.containerDomNode.clientWidth / 2) - 3}px`;
|
||||
this._hoverPointer.style.left = `${targetBounds[0].width / 2}px`;
|
||||
}
|
||||
|
||||
else if (this._hoverPosition === HoverPosition.ABOVE) {
|
||||
this._hoverPointer.classList.add('bottom');
|
||||
this._hoverPointer.style.left = `${(this._hover.containerDomNode.clientWidth / 2) - 3}px`;
|
||||
this._hoverPointer.style.left = `${targetBounds[0].width / 2}px`;
|
||||
}
|
||||
this.setHoverPointerPosition(targetRect);
|
||||
}
|
||||
|
||||
this._hover.onContentsChanged();
|
||||
}
|
||||
|
||||
private computeXCordinate(target: TargetRect): void {
|
||||
if (this._target.x !== undefined) {
|
||||
this._x = this._target.x;
|
||||
}
|
||||
|
||||
else if (this._hoverPosition === HoverPosition.RIGHT) {
|
||||
this._x = target.right;
|
||||
}
|
||||
|
||||
else if (this._hoverPosition === HoverPosition.LEFT) {
|
||||
this._x = target.left;
|
||||
}
|
||||
|
||||
else {
|
||||
if (this._hoverPointer) {
|
||||
this._x = target.center.x - (this._hover.containerDomNode.clientWidth / 2);
|
||||
} else {
|
||||
if (target.left + this._hover.containerDomNode.clientWidth >= document.documentElement.clientWidth) {
|
||||
this._hover.containerDomNode.classList.add('right-aligned');
|
||||
this._x = document.documentElement.clientWidth - this._workbenchLayoutService.getWindowBorderWidth() - 1;
|
||||
} else {
|
||||
this._x = target.left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hover on left is going beyond window
|
||||
if (this._x < document.documentElement.clientLeft) {
|
||||
this._x = target.left;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private computeYCordinate(target: TargetRect): void {
|
||||
if (this._target.y !== undefined) {
|
||||
this._y = this._target.y;
|
||||
}
|
||||
|
||||
else if (this._hoverPosition === HoverPosition.ABOVE) {
|
||||
this._y = target.top;
|
||||
}
|
||||
|
||||
else if (this._hoverPosition === HoverPosition.BELOW) {
|
||||
this._y = target.bottom - 2;
|
||||
}
|
||||
|
||||
else {
|
||||
if (this._hoverPointer) {
|
||||
this._y = target.center.y + (this._hover.containerDomNode.clientHeight / 2);
|
||||
} else {
|
||||
this._y = target.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover on bottom is going beyond window
|
||||
if (this._y > window.innerHeight) {
|
||||
this._y = target.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
private adjustHorizontalHoverPosition(target: TargetRect): void {
|
||||
// Do not adjust horizontal hover position if x cordiante is provided
|
||||
if (this._target.x !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Position hover on right to target
|
||||
if (this._hoverPosition === HoverPosition.RIGHT) {
|
||||
// Hover on the right is going beyond window.
|
||||
if (target.right + this._hover.containerDomNode.clientWidth >= document.documentElement.clientWidth) {
|
||||
this._hoverPosition = HoverPosition.LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
// Position hover on left to target
|
||||
if (this._hoverPosition === HoverPosition.LEFT) {
|
||||
// Hover on the left is going beyond window.
|
||||
if (target.left - this._hover.containerDomNode.clientWidth <= document.documentElement.clientLeft) {
|
||||
this._hoverPosition = HoverPosition.RIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private adjustVerticalHoverPosition(target: TargetRect): void {
|
||||
// Do not adjust vertical hover position if y cordiante is provided
|
||||
if (this._target.y !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Position hover on top of the target
|
||||
if (this._hoverPosition === HoverPosition.ABOVE) {
|
||||
// Hover on top is going beyond window
|
||||
if (target.top - this._hover.containerDomNode.clientHeight < 0) {
|
||||
this._hoverPosition = HoverPosition.BELOW;
|
||||
}
|
||||
}
|
||||
|
||||
// Position hover below the target
|
||||
else if (this._hoverPosition === HoverPosition.BELOW) {
|
||||
// Hover on bottom is going beyond window
|
||||
if (target.bottom + this._hover.containerDomNode.clientHeight > window.innerHeight) {
|
||||
this._hoverPosition = HoverPosition.ABOVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setHoverPointerPosition(target: TargetRect): void {
|
||||
if (!this._hoverPointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this._hoverPosition) {
|
||||
case HoverPosition.LEFT:
|
||||
case HoverPosition.RIGHT:
|
||||
this._hoverPointer.classList.add(this._hoverPosition === HoverPosition.LEFT ? 'right' : 'left');
|
||||
const hoverHeight = this._hover.containerDomNode.clientHeight;
|
||||
|
||||
// If hover is taller than target and aligned with target's bottom, then show the pointer at the center of target
|
||||
if (hoverHeight > target.height && this._y === target.bottom) {
|
||||
this._hoverPointer.style.top = `${target.center.y - target.top - 3}px`;
|
||||
}
|
||||
|
||||
// Otherwise show the pointer at the center of hover
|
||||
else {
|
||||
this._hoverPointer.style.top = `${(hoverHeight / 2) - 3}px`;
|
||||
}
|
||||
|
||||
break;
|
||||
case HoverPosition.ABOVE:
|
||||
case HoverPosition.BELOW:
|
||||
this._hoverPointer.classList.add(this._hoverPosition === HoverPosition.ABOVE ? 'bottom' : 'top');
|
||||
const hoverWidth = this._hover.containerDomNode.clientWidth;
|
||||
|
||||
// If hover is wider than target and aligned with target's left, then show the pointer at the center of target
|
||||
if (hoverWidth > target.width && this._x === target.left) {
|
||||
this._hoverPointer.style.left = `${target.center.x - target.left - 3}px`;
|
||||
}
|
||||
|
||||
// Otherwise show the pointer at the center of hover
|
||||
else {
|
||||
this._hoverPointer.style.left = `${(hoverWidth / 2) - 3}px`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this._hover.containerDomNode.focus();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue