Skip to content

Commit 5089503

Browse files
committed
Fixes microsoft#83150: Handle case where the mouse is hitting the textarea when the textarea is rendered at the cursor
1 parent e038d4a commit 5089503

4 files changed

Lines changed: 48 additions & 21 deletions

File tree

src/vs/editor/browser/controller/mouseHandler.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@ import { StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent
99
import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async';
1010
import { Disposable } from 'vs/base/common/lifecycle';
1111
import * as platform from 'vs/base/common/platform';
12-
import { HitTestContext, IViewZoneData, MouseTarget, MouseTargetFactory } from 'vs/editor/browser/controller/mouseTarget';
12+
import { HitTestContext, IViewZoneData, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget';
1313
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
1414
import { ClientCoordinates, EditorMouseEvent, EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, createEditorPagePosition } from 'vs/editor/browser/editorDom';
1515
import { ViewController } from 'vs/editor/browser/view/viewController';
16-
import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor';
1716
import { EditorZoom } from 'vs/editor/common/config/editorZoom';
1817
import { Position } from 'vs/editor/common/core/position';
1918
import { Selection } from 'vs/editor/common/core/selection';
@@ -46,9 +45,9 @@ export interface IPointerHandlerHelper {
4645
focusTextArea(): void;
4746

4847
/**
49-
* Get the last rendered information of the cursors.
48+
* Get the last rendered information for cursors & textarea.
5049
*/
51-
getLastViewCursorsRenderData(): IViewCursorRenderData[];
50+
getLastRenderData(): PointerHandlerLastRenderData;
5251

5352
shouldSuppressMouseDownOnViewZone(viewZoneId: string): boolean;
5453
shouldSuppressMouseDownOnWidget(widgetId: string): boolean;
@@ -158,13 +157,11 @@ export class MouseHandler extends ViewEventHandler {
158157
return null;
159158
}
160159

161-
const lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData();
162-
return this.mouseTargetFactory.createMouseTarget(lastViewCursorsRenderData, editorPos, pos, null);
160+
return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), editorPos, pos, null);
163161
}
164162

165163
protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): editorBrowser.IMouseTarget {
166-
const lastViewCursorsRenderData = this.viewHelper.getLastViewCursorsRenderData();
167-
return this.mouseTargetFactory.createMouseTarget(lastViewCursorsRenderData, e.editorPos, e.pos, testEventTarget ? e.target : null);
164+
return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, testEventTarget ? e.target : null);
168165
}
169166

170167
private _getMouseColumn(e: EditorMouseEvent): number {

src/vs/editor/browser/controller/mouseTarget.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ interface IHitTestResult {
8989
hitTarget: Element | null;
9090
}
9191

92+
export class PointerHandlerLastRenderData {
93+
constructor(
94+
public readonly lastViewCursorsRenderData: IViewCursorRenderData[],
95+
public readonly lastTextareaPosition: Position | null
96+
) { }
97+
}
98+
9299
export class MouseTarget implements IMouseTarget {
93100

94101
public readonly element: Element | null;
@@ -232,19 +239,19 @@ export class HitTestContext {
232239
public readonly viewDomNode: HTMLElement;
233240
public readonly lineHeight: number;
234241
public readonly typicalHalfwidthCharacterWidth: number;
235-
public readonly lastViewCursorsRenderData: IViewCursorRenderData[];
242+
public readonly lastRenderData: PointerHandlerLastRenderData;
236243

237244
private readonly _context: ViewContext;
238245
private readonly _viewHelper: IPointerHandlerHelper;
239246

240-
constructor(context: ViewContext, viewHelper: IPointerHandlerHelper, lastViewCursorsRenderData: IViewCursorRenderData[]) {
247+
constructor(context: ViewContext, viewHelper: IPointerHandlerHelper, lastRenderData: PointerHandlerLastRenderData) {
241248
this.model = context.model;
242249
const options = context.configuration.options;
243250
this.layoutInfo = options.get(EditorOption.layoutInfo);
244251
this.viewDomNode = viewHelper.viewDomNode;
245252
this.lineHeight = options.get(EditorOption.lineHeight);
246253
this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth;
247-
this.lastViewCursorsRenderData = lastViewCursorsRenderData;
254+
this.lastRenderData = lastRenderData;
248255
this._context = context;
249256
this._viewHelper = viewHelper;
250257
}
@@ -462,8 +469,8 @@ export class MouseTargetFactory {
462469
return false;
463470
}
464471

465-
public createMouseTarget(lastViewCursorsRenderData: IViewCursorRenderData[], editorPos: EditorPagePosition, pos: PageCoordinates, target: HTMLElement | null): IMouseTarget {
466-
const ctx = new HitTestContext(this._context, this._viewHelper, lastViewCursorsRenderData);
472+
public createMouseTarget(lastRenderData: PointerHandlerLastRenderData, editorPos: EditorPagePosition, pos: PageCoordinates, target: HTMLElement | null): IMouseTarget {
473+
const ctx = new HitTestContext(this._context, this._viewHelper, lastRenderData);
467474
const request = new HitTestRequest(ctx, editorPos, pos, target);
468475
try {
469476
const r = MouseTargetFactory._createMouseTarget(ctx, request, false);
@@ -544,7 +551,7 @@ export class MouseTargetFactory {
544551

545552
if (request.target) {
546553
// Check if we've hit a painted cursor
547-
const lastViewCursorsRenderData = ctx.lastViewCursorsRenderData;
554+
const lastViewCursorsRenderData = ctx.lastRenderData.lastViewCursorsRenderData;
548555

549556
for (const d of lastViewCursorsRenderData) {
550557

@@ -560,7 +567,7 @@ export class MouseTargetFactory {
560567
// first or last rendered view line dom node, therefore help it out
561568
// and first check if we are on top of a cursor
562569

563-
const lastViewCursorsRenderData = ctx.lastViewCursorsRenderData;
570+
const lastViewCursorsRenderData = ctx.lastRenderData.lastViewCursorsRenderData;
564571
const mouseContentHorizontalOffset = request.mouseContentHorizontalOffset;
565572
const mouseVerticalOffset = request.mouseVerticalOffset;
566573

@@ -602,7 +609,10 @@ export class MouseTargetFactory {
602609
private static _hitTestTextArea(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null {
603610
// Is it the textarea?
604611
if (ElementPath.isTextArea(request.targetPath)) {
605-
return request.fulfill(MouseTargetType.TEXTAREA);
612+
if (ctx.lastRenderData.lastTextareaPosition) {
613+
return request.fulfill(MouseTargetType.CONTENT_TEXT, ctx.lastRenderData.lastTextareaPosition);
614+
}
615+
return request.fulfill(MouseTargetType.TEXTAREA, ctx.lastRenderData.lastTextareaPosition);
606616
}
607617
return null;
608618
}

src/vs/editor/browser/controller/textAreaHandler.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ export class TextAreaHandler extends ViewPart {
7777
private _visibleTextArea: VisibleTextAreaData | null;
7878
private _selections: Selection[];
7979

80+
/**
81+
* The position at which the textarea was rendered.
82+
* This is useful for hit-testing and determining the mouse position.
83+
*/
84+
private _lastRenderPosition: Position | null;
85+
8086
public readonly textArea: FastDomNode<HTMLTextAreaElement>;
8187
public readonly textAreaCover: FastDomNode<HTMLElement>;
8288
private readonly _textAreaInput: TextAreaInput;
@@ -104,6 +110,7 @@ export class TextAreaHandler extends ViewPart {
104110

105111
this._visibleTextArea = null;
106112
this._selections = [new Selection(1, 1, 1, 1)];
113+
this._lastRenderPosition = null;
107114

108115
// Text Area (The focus will always be in the textarea when the cursor is blinking)
109116
this.textArea = createFastDomNode(document.createElement('textarea'));
@@ -413,13 +420,18 @@ export class TextAreaHandler extends ViewPart {
413420
this._textAreaInput.refreshFocusState();
414421
}
415422

423+
public getLastRenderData(): Position | null {
424+
return this._lastRenderPosition;
425+
}
426+
416427
// --- end view API
417428

429+
private _primaryCursorPosition: Position = new Position(1, 1);
418430
private _primaryCursorVisibleRange: HorizontalPosition | null = null;
419431

420432
public prepareRender(ctx: RenderingContext): void {
421-
const primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn);
422-
this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(primaryCursorPosition);
433+
this._primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn);
434+
this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(this._primaryCursorPosition);
423435
}
424436

425437
public render(ctx: RestrictedRenderingContext): void {
@@ -431,6 +443,7 @@ export class TextAreaHandler extends ViewPart {
431443
if (this._visibleTextArea) {
432444
// The text area is visible for composition reasons
433445
this._renderInsideEditor(
446+
null,
434447
this._visibleTextArea.top - this._scrollTop,
435448
this._contentLeft + this._visibleTextArea.left - this._scrollLeft,
436449
this._visibleTextArea.width,
@@ -465,19 +478,22 @@ export class TextAreaHandler extends ViewPart {
465478
// For the popup emoji input, we will make the text area as high as the line height
466479
// We will also make the fontSize and lineHeight the correct dimensions to help with the placement of these pickers
467480
this._renderInsideEditor(
481+
this._primaryCursorPosition,
468482
top, left,
469483
canUseZeroSizeTextarea ? 0 : 1, this._lineHeight
470484
);
471485
return;
472486
}
473487

474488
this._renderInsideEditor(
489+
this._primaryCursorPosition,
475490
top, left,
476491
canUseZeroSizeTextarea ? 0 : 1, canUseZeroSizeTextarea ? 0 : 1
477492
);
478493
}
479494

480-
private _renderInsideEditor(top: number, left: number, width: number, height: number): void {
495+
private _renderInsideEditor(renderedPosition: Position | null, top: number, left: number, width: number, height: number): void {
496+
this._lastRenderPosition = renderedPosition;
481497
const ta = this.textArea;
482498
const tac = this.textAreaCover;
483499

@@ -495,6 +511,7 @@ export class TextAreaHandler extends ViewPart {
495511
}
496512

497513
private _renderAtTopLeft(): void {
514+
this._lastRenderPosition = null;
498515
const ta = this.textArea;
499516
const tac = this.textAreaCover;
500517

src/vs/editor/browser/view/viewImpl.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler';
4848
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
4949
import { IThemeService, getThemeTypeSelector } from 'vs/platform/theme/common/themeService';
5050
import { EditorOption } from 'vs/editor/common/config/editorOptions';
51+
import { PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget';
5152

5253

5354
export interface IContentWidgetData {
@@ -244,8 +245,10 @@ export class View extends ViewEventHandler {
244245
this.focus();
245246
},
246247

247-
getLastViewCursorsRenderData: () => {
248-
return this.viewCursors.getLastRenderData() || [];
248+
getLastRenderData: (): PointerHandlerLastRenderData => {
249+
const lastViewCursorsRenderData = this.viewCursors.getLastRenderData() || [];
250+
const lastTextareaPosition = this._textAreaHandler.getLastRenderData();
251+
return new PointerHandlerLastRenderData(lastViewCursorsRenderData, lastTextareaPosition);
249252
},
250253
shouldSuppressMouseDownOnViewZone: (viewZoneId: string) => {
251254
return this.viewZones.shouldSuppressMouseDownOnViewZone(viewZoneId);

0 commit comments

Comments
 (0)