Skip to content

Commit

Permalink
Fixing hover disappearance in chat pannel (microsoft#219099)
Browse files Browse the repository at this point in the history
* adding code

* adding code to send the event at the lowest level

* adding code

* adding some code

* polish

* fixing hover disappearing too quickly, not looking at delay

* polishing the code

* cleaning

* adding cleaning code

* settign timeout to 0
  • Loading branch information
aiday-mar authored Jul 22, 2024
1 parent b97d0a2 commit 5ff3c23
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 27 deletions.
91 changes: 72 additions & 19 deletions src/vs/editor/browser/controller/mouseHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableEle

export interface IPointerHandlerHelper {
viewDomNode: HTMLElement;
overflowWidgetsDomNode: HTMLElement | null;
linesContentDomNode: HTMLElement;
viewLinesDomNode: HTMLElement;

Expand Down Expand Up @@ -62,6 +63,8 @@ export class MouseHandler extends ViewEventHandler {
private lastMouseLeaveTime: number;
private _height: number;
private _mouseLeaveMonitor: IDisposable | null = null;
private _mouseOnOverflowWidgetsDomNode: boolean = false;
private _mouseOnViewDomNode: boolean = false;

constructor(context: ViewContext, viewController: ViewController, viewHelper: IPointerHandlerHelper) {
super();
Expand All @@ -76,7 +79,7 @@ export class MouseHandler extends ViewEventHandler {
this.viewController,
this.viewHelper,
this.mouseTargetFactory,
(e, testEventTarget) => this._createMouseTarget(e, testEventTarget),
(e, testEventTarget) => this._createMouseTargetForView(e, testEventTarget),
(e) => this._getMouseColumn(e)
));

Expand All @@ -88,7 +91,8 @@ export class MouseHandler extends ViewEventHandler {
this._register(mouseEvents.onContextMenu(this.viewHelper.viewDomNode, (e) => this._onContextMenu(e, true)));

this._register(mouseEvents.onMouseMove(this.viewHelper.viewDomNode, (e) => {
this._onMouseMove(e);
this._mouseOnViewDomNode = true;
this._onMouseMoveOverView(e);

// See https://github.com/microsoft/vscode/issues/138789
// When moving the mouse really quickly, the browser sometimes forgets to
Expand All @@ -101,15 +105,45 @@ export class MouseHandler extends ViewEventHandler {
this._mouseLeaveMonitor = dom.addDisposableListener(this.viewHelper.viewDomNode.ownerDocument, 'mousemove', (e) => {
if (!this.viewHelper.viewDomNode.contains(e.target as Node | null)) {
// went outside the editor!
this._onMouseLeave(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode));
this._mouseOnViewDomNode = false;
setTimeout(() => {
if (!this._mouseOnOverflowWidgetsDomNode) {
this._onMouseLeave(new EditorMouseEvent(e, false, this.viewHelper.viewDomNode));
}
}, 0);
}
});
}
}));

this._register(mouseEvents.onMouseUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e)));

this._register(mouseEvents.onMouseLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e)));
this._register(mouseEvents.onMouseLeave(this.viewHelper.viewDomNode, (e) => {
this._mouseOnViewDomNode = false;
setTimeout(() => {
if (!this._mouseOnOverflowWidgetsDomNode) {
this._onMouseLeave(e);
}
}, 0);
}));

const overflowWidgetsDomNode = this.viewHelper.overflowWidgetsDomNode;
if (overflowWidgetsDomNode) {
this._register(mouseEvents.onMouseMove(overflowWidgetsDomNode, (e) => {
this._mouseOnOverflowWidgetsDomNode = true;
this._mouseLeaveMonitor?.dispose();
this._mouseLeaveMonitor = null;
this._onMouseMoveOverOverflowWidgetsDomNode(e);
}));
this._register(mouseEvents.onMouseLeave(overflowWidgetsDomNode, (e) => {
this._mouseOnOverflowWidgetsDomNode = false;
setTimeout(() => {
if (!this._mouseOnViewDomNode) {
this._onMouseLeave(e);
}
}, 0);
}));
}

// `pointerdown` events can't be used to determine if there's a double click, or triple click
// because their `e.detail` is always 0.
Expand Down Expand Up @@ -234,10 +268,10 @@ export class MouseHandler extends ViewEventHandler {
}

const relativePos = createCoordinatesRelativeToEditor(this.viewHelper.viewDomNode, editorPos, pos);
return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), editorPos, pos, relativePos, null);
return this.mouseTargetFactory.createMouseTargetForView(this.viewHelper.getLastRenderData(), editorPos, pos, relativePos, null);
}

protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): IMouseTarget {
protected _createMouseTargetForView(e: EditorMouseEvent, testEventTarget: boolean): IMouseTarget {
let target = e.target;
if (!this.viewHelper.viewDomNode.contains(target)) {
const shadowRoot = dom.getShadowRoot(this.viewHelper.viewDomNode);
Expand All @@ -247,7 +281,11 @@ export class MouseHandler extends ViewEventHandler {
);
}
}
return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, e.relativePos, testEventTarget ? target : null);
return this.mouseTargetFactory.createMouseTargetForView(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, e.relativePos, testEventTarget ? target : null);
}

private _createMouseTargetForOverflowWidgetsDomNode(e: EditorMouseEvent): IMouseTarget {
return this.mouseTargetFactory.createMouseTargetForOverflowWidgetsDomNode(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, e.relativePos, e.target);
}

private _getMouseColumn(e: EditorMouseEvent): number {
Expand All @@ -257,30 +295,45 @@ export class MouseHandler extends ViewEventHandler {
protected _onContextMenu(e: EditorMouseEvent, testEventTarget: boolean): void {
this.viewController.emitContextMenu({
event: e,
target: this._createMouseTarget(e, testEventTarget)
target: this._createMouseTargetForView(e, testEventTarget)
});
}

protected _onMouseMoveOverView(e: EditorMouseEvent): void {
this._onMouseMove(e, this._createMouseTargetForView(e, true));
}

private _onMouseMoveOverOverflowWidgetsDomNode(e: EditorMouseEvent): void {
this._onMouseMove(e, this._createMouseTargetForOverflowWidgetsDomNode(e));
}

private _onMouseMove(e: EditorMouseEvent, target: IMouseTarget): void {
const shouldIgnoreMouseMoveEvent = this._shouldIgnoreMouseMoveEvent(e);
if (shouldIgnoreMouseMoveEvent) {
return undefined;
}
this.viewController.emitMouseMove({
event: e,
target
});
}

protected _onMouseMove(e: EditorMouseEvent): void {
private _shouldIgnoreMouseMoveEvent(e: EditorMouseEvent): boolean {
const targetIsWidget = this.mouseTargetFactory.mouseTargetIsWidget(e);
if (!targetIsWidget) {
e.preventDefault();
}

if (this._mouseDownOperation.isActive()) {
// In selection/drag operation
return;
return true;
}
const actualMouseMoveTime = e.timestamp;
if (actualMouseMoveTime < this.lastMouseLeaveTime) {
// Due to throttling, this event occurred before the mouse left the editor, therefore ignore it.
return;
return true;
}

this.viewController.emitMouseMove({
event: e,
target: this._createMouseTarget(e, true)
});
return false;
}

protected _onMouseLeave(e: EditorMouseEvent): void {
Expand All @@ -298,12 +351,12 @@ export class MouseHandler extends ViewEventHandler {
protected _onMouseUp(e: EditorMouseEvent): void {
this.viewController.emitMouseUp({
event: e,
target: this._createMouseTarget(e, true)
target: this._createMouseTargetForView(e, true)
});
}

protected _onMouseDown(e: EditorMouseEvent, pointerId: number): void {
const t = this._createMouseTarget(e, true);
const t = this._createMouseTargetForView(e, true);

const targetIsContent = (t.type === MouseTargetType.CONTENT_TEXT || t.type === MouseTargetType.CONTENT_EMPTY);
const targetIsGutter = (t.type === MouseTargetType.GUTTER_GLYPH_MARGIN || t.type === MouseTargetType.GUTTER_LINE_NUMBERS || t.type === MouseTargetType.GUTTER_LINE_DECORATIONS);
Expand Down Expand Up @@ -742,7 +795,7 @@ class TopBottomDragScrollingOperation extends Disposable {
const horizontalScrollbarHeight = this._context.configuration.options.get(EditorOption.layoutInfo).horizontalScrollbarHeight;
const pos = new PageCoordinates(this._mouseEvent.pos.x, editorPos.y + editorPos.height - horizontalScrollbarHeight - 0.1);
const relativePos = createCoordinatesRelativeToEditor(this._viewHelper.viewDomNode, editorPos, pos);
mouseTarget = this._mouseTargetFactory.createMouseTarget(this._viewHelper.getLastRenderData(), editorPos, pos, relativePos, null);
mouseTarget = this._mouseTargetFactory.createMouseTargetForView(this._viewHelper.getLastRenderData(), editorPos, pos, relativePos, null);
}
if (!mouseTarget.position || mouseTarget.position.lineNumber !== edgeLineNumber) {
if (this._position.outsidePosition === 'above') {
Expand Down
24 changes: 19 additions & 5 deletions src/vs/editor/browser/controller/mouseTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ export class HitTestContext {
public readonly viewModel: IViewModel;
public readonly layoutInfo: EditorLayoutInfo;
public readonly viewDomNode: HTMLElement;
public readonly overflowWidgetsDomNode: HTMLElement | null;
public readonly lineHeight: number;
public readonly stickyTabStops: boolean;
public readonly typicalHalfwidthCharacterWidth: number;
Expand All @@ -251,6 +252,7 @@ export class HitTestContext {
const options = context.configuration.options;
this.layoutInfo = options.get(EditorOption.layoutInfo);
this.viewDomNode = viewHelper.viewDomNode;
this.overflowWidgetsDomNode = viewHelper.overflowWidgetsDomNode ?? null;
this.lineHeight = options.get(EditorOption.lineHeight);
this.stickyTabStops = options.get(EditorOption.stickyTabStops);
this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth;
Expand Down Expand Up @@ -413,6 +415,7 @@ class HitTestRequest extends BareHitTestRequest {
private _useHitTestTarget: boolean;
private _targetPathCacheElement: HTMLElement | null = null;
private _targetPathCacheValue: Uint8Array = new Uint8Array(0);
private _targetElement: HTMLElement | null = null;

public get target(): HTMLElement | null {
if (this._useHitTestTarget) {
Expand All @@ -422,17 +425,18 @@ class HitTestRequest extends BareHitTestRequest {
}

public get targetPath(): Uint8Array {
if (this._targetPathCacheElement !== this.target) {
if (this._targetPathCacheElement !== this.target && this._targetElement) {
this._targetPathCacheElement = this.target;
this._targetPathCacheValue = PartFingerprints.collect(this.target, this._ctx.viewDomNode);
this._targetPathCacheValue = PartFingerprints.collect(this.target, this._targetElement);
}
return this._targetPathCacheValue;
}

constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, eventTarget: HTMLElement | null) {
constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, eventTarget: HTMLElement | null, targetElement: HTMLElement | null = null) {
super(ctx, editorPos, pos, relativePos);
this._ctx = ctx;
this._eventTarget = eventTarget;
this._targetElement = targetElement;

// If no event target is passed in, we will use the hit test target
const hasEventTarget = Boolean(this._eventTarget);
Expand Down Expand Up @@ -532,9 +536,9 @@ export class MouseTargetFactory {
return false;
}

public createMouseTarget(lastRenderData: PointerHandlerLastRenderData, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, target: HTMLElement | null): IMouseTarget {
public createMouseTargetForView(lastRenderData: PointerHandlerLastRenderData, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, target: HTMLElement | null): IMouseTarget {
const ctx = new HitTestContext(this._context, this._viewHelper, lastRenderData);
const request = new HitTestRequest(ctx, editorPos, pos, relativePos, target);
const request = new HitTestRequest(ctx, editorPos, pos, relativePos, target, ctx.viewDomNode);
try {
const r = MouseTargetFactory._createMouseTarget(ctx, request);

Expand All @@ -555,6 +559,16 @@ export class MouseTargetFactory {
}
}

public createMouseTargetForOverflowWidgetsDomNode(lastRenderData: PointerHandlerLastRenderData, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, target: HTMLElement | null) {
const ctx = new HitTestContext(this._context, this._viewHelper, lastRenderData);
const request = new HitTestRequest(ctx, editorPos, pos, relativePos, target, ctx.overflowWidgetsDomNode);
try {
return MouseTargetFactory._createMouseTarget(ctx, request);
} catch (err) {
return request.fulfillUnknown();
}
}

private static _createMouseTarget(ctx: HitTestContext, request: HitTestRequest): IMouseTarget {

// console.log(`${domHitTestExecuted ? '=>' : ''}CAME IN REQUEST: ${request}`);
Expand Down
6 changes: 3 additions & 3 deletions src/vs/editor/browser/controller/pointerHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class PointerEventHandler extends MouseHandler {
// PonterEvents
const pointerEvents = new EditorPointerEventFactory(this.viewHelper.viewDomNode);

this._register(pointerEvents.onPointerMove(this.viewHelper.viewDomNode, (e) => this._onMouseMove(e)));
this._register(pointerEvents.onPointerMove(this.viewHelper.viewDomNode, (e) => this._onMouseMoveOverView(e)));
this._register(pointerEvents.onPointerUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e)));
this._register(pointerEvents.onPointerLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e)));
this._register(pointerEvents.onPointerDown(this.viewHelper.viewDomNode, (e, pointerId) => this._onMouseDown(e, pointerId)));
Expand All @@ -73,7 +73,7 @@ export class PointerEventHandler extends MouseHandler {
}

private _dispatchGesture(event: GestureEvent, inSelectionMode: boolean): void {
const target = this._createMouseTarget(new EditorMouseEvent(event, false, this.viewHelper.viewDomNode), false);
const target = this._createMouseTargetForView(new EditorMouseEvent(event, false, this.viewHelper.viewDomNode), false);
if (target.position) {
this.viewController.dispatchMouse({
position: target.position,
Expand Down Expand Up @@ -119,7 +119,7 @@ class TouchHandler extends MouseHandler {

this.viewHelper.focusTextArea();

const target = this._createMouseTarget(new EditorMouseEvent(event, false, this.viewHelper.viewDomNode), false);
const target = this._createMouseTargetForView(new EditorMouseEvent(event, false, this.viewHelper.viewDomNode), false);

if (target.position) {
// Send the tap event also to the <textarea> (for input purposes)
Expand Down
3 changes: 3 additions & 0 deletions src/vs/editor/browser/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export class View extends ViewEventHandler {
private readonly _linesContent: FastDomNode<HTMLElement>;
public readonly domNode: FastDomNode<HTMLElement>;
private readonly _overflowGuardContainer: FastDomNode<HTMLElement>;
private readonly _overflowWidgetsDomNode: HTMLElement | null;

// Actual mutable state
private _shouldRecomputeGlyphMarginLanes: boolean = false;
Expand All @@ -114,6 +115,7 @@ export class View extends ViewEventHandler {
super();
this._selections = [new Selection(1, 1, 1, 1)];
this._renderAnimationFrame = null;
this._overflowWidgetsDomNode = overflowWidgetsDomNode ?? null;

const viewController = new ViewController(configuration, model, userInputEvents, commandDelegate);

Expand Down Expand Up @@ -278,6 +280,7 @@ export class View extends ViewEventHandler {
private _createPointerHandlerHelper(): IPointerHandlerHelper {
return {
viewDomNode: this.domNode.domNode,
overflowWidgetsDomNode: this._overflowWidgetsDomNode,
linesContentDomNode: this._linesContent.domNode,
viewLinesDomNode: this._viewLines.getDomNode().domNode,

Expand Down

0 comments on commit 5ff3c23

Please sign in to comment.