Merge pull request #128239 from microsoft/hediet/mouse-hit-detection-injected-text
Sets mightBeForeignElement to true when mouse is on injected text.
This commit is contained in:
commit
951d1c7030
4 changed files with 70 additions and 16 deletions
|
@ -14,7 +14,7 @@ import { Position } from 'vs/editor/common/core/position';
|
|||
import { Range as EditorRange } from 'vs/editor/common/core/range';
|
||||
import { HorizontalPosition } from 'vs/editor/common/view/renderingContext';
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { InjectedText, IViewModel } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { CursorColumns } from 'vs/editor/common/controller/cursorCommon';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations';
|
||||
|
@ -61,7 +61,8 @@ class ContentHitTestResult {
|
|||
readonly type = HitTestResultType.Content;
|
||||
constructor(
|
||||
readonly position: Position,
|
||||
readonly spanNode: HTMLElement
|
||||
readonly spanNode: HTMLElement,
|
||||
readonly injectedText: InjectedText | null,
|
||||
) { }
|
||||
}
|
||||
|
||||
|
@ -71,7 +72,7 @@ namespace HitTestResult {
|
|||
export function createFromDOMInfo(ctx: HitTestContext, spanNode: HTMLElement, offset: number): HitTestResult {
|
||||
const position = ctx.getPositionFromDOMInfo(spanNode, offset);
|
||||
if (position) {
|
||||
return new ContentHitTestResult(position, spanNode);
|
||||
return new ContentHitTestResult(position, spanNode, null);
|
||||
}
|
||||
return new UnknownHitTestResult(spanNode);
|
||||
}
|
||||
|
@ -505,7 +506,7 @@ export class MouseTargetFactory {
|
|||
const hitTestResult = MouseTargetFactory._doHitTest(ctx, request);
|
||||
|
||||
if (hitTestResult.type === HitTestResultType.Content) {
|
||||
return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.spanNode, hitTestResult.position);
|
||||
return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.spanNode, hitTestResult.position, hitTestResult.injectedText);
|
||||
}
|
||||
|
||||
return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true);
|
||||
|
@ -702,7 +703,7 @@ export class MouseTargetFactory {
|
|||
const hitTestResult = MouseTargetFactory._doHitTest(ctx, request);
|
||||
|
||||
if (hitTestResult.type === HitTestResultType.Content) {
|
||||
return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.spanNode, hitTestResult.position);
|
||||
return MouseTargetFactory.createMouseTargetFromHitTestPosition(ctx, request, hitTestResult.spanNode, hitTestResult.position, hitTestResult.injectedText);
|
||||
}
|
||||
|
||||
return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true);
|
||||
|
@ -758,7 +759,7 @@ export class MouseTargetFactory {
|
|||
return (chars + 1);
|
||||
}
|
||||
|
||||
private static createMouseTargetFromHitTestPosition(ctx: HitTestContext, request: HitTestRequest, spanNode: HTMLElement, pos: Position): MouseTarget {
|
||||
private static createMouseTargetFromHitTestPosition(ctx: HitTestContext, request: HitTestRequest, spanNode: HTMLElement, pos: Position, injectedText: InjectedText | null): MouseTarget {
|
||||
const lineNumber = pos.lineNumber;
|
||||
const column = pos.column;
|
||||
|
||||
|
@ -778,7 +779,7 @@ export class MouseTargetFactory {
|
|||
const columnHorizontalOffset = visibleRange.left;
|
||||
|
||||
if (request.mouseContentHorizontalOffset === columnHorizontalOffset) {
|
||||
return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, null, { mightBeForeignElement: false });
|
||||
return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, null, { mightBeForeignElement: !!injectedText });
|
||||
}
|
||||
|
||||
// Let's define a, b, c and check if the offset is in between them...
|
||||
|
@ -811,10 +812,10 @@ export class MouseTargetFactory {
|
|||
const curr = points[i];
|
||||
if (prev.offset <= request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset <= curr.offset) {
|
||||
const rng = new EditorRange(lineNumber, prev.column, lineNumber, curr.column);
|
||||
return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, rng, { mightBeForeignElement: !mouseIsOverSpanNode });
|
||||
return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, rng, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText });
|
||||
}
|
||||
}
|
||||
return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, null, { mightBeForeignElement: !mouseIsOverSpanNode });
|
||||
return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, null, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -957,14 +958,16 @@ export class MouseTargetFactory {
|
|||
result = this._doHitTestWithCaretPositionFromPoint(ctx, request.pos.toClientCoordinates());
|
||||
}
|
||||
if (result.type === HitTestResultType.Content) {
|
||||
const injectedText = ctx.model.getInjectedTextAt(result.position);
|
||||
|
||||
const normalizedPosition = ctx.model.normalizePosition(result.position, PositionAffinity.None);
|
||||
if (!normalizedPosition.equals(result.position)) {
|
||||
result = new ContentHitTestResult(normalizedPosition, result.spanNode);
|
||||
if (injectedText || !normalizedPosition.equals(result.position)) {
|
||||
result = new ContentHitTestResult(normalizedPosition, result.spanNode, injectedText);
|
||||
}
|
||||
}
|
||||
// Snap to the nearest soft tab boundary if atomic soft tabs are enabled.
|
||||
if (result.type === HitTestResultType.Content && ctx.stickyTabStops) {
|
||||
result = new ContentHitTestResult(this._snapToSoftTabBoundary(result.position, ctx.model), result.spanNode);
|
||||
result = new ContentHitTestResult(this._snapToSoftTabBoundary(result.position, ctx.model), result.spanNode, result.injectedText);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { EndOfLinePreference, IActiveIndentGuideInfo, IModelDecoration, IModelDe
|
|||
import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { PrefixSumIndexOfResult } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { ICoordinatesConverter, ILineBreaksComputer, IOverviewRulerDecorations, LineBreakData, SingleLineInlineDecoration, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ICoordinatesConverter, InjectedText, ILineBreaksComputer, IOverviewRulerDecorations, LineBreakData, SingleLineInlineDecoration, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { FontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { EditorTheme } from 'vs/editor/common/view/viewContext';
|
||||
|
@ -48,6 +48,8 @@ export interface ISplitLine {
|
|||
getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number, affinity?: PositionAffinity): Position;
|
||||
getViewLineNumberOfModelPosition(deltaLineNumber: number, inputColumn: number): number;
|
||||
normalizePosition(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number, outputPosition: Position, affinity: PositionAffinity): Position;
|
||||
|
||||
getInjectedTextAt(outputLineIndex: number, column: number): InjectedText | null;
|
||||
}
|
||||
|
||||
export interface IViewModelLinesCollection extends IDisposable {
|
||||
|
@ -78,6 +80,8 @@ export interface IViewModelLinesCollection extends IDisposable {
|
|||
getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: EditorTheme): IOverviewRulerDecorations;
|
||||
getDecorationsInRange(range: Range, ownerId: number, filterOutValidation: boolean): IModelDecoration[];
|
||||
|
||||
getInjectedTextAt(viewPosition: Position): InjectedText | null;
|
||||
|
||||
normalizePosition(position: Position, affinity: PositionAffinity): Position;
|
||||
/**
|
||||
* Gets the column at which indentation stops at a given line.
|
||||
|
@ -997,6 +1001,15 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
|
|||
return finalResult;
|
||||
}
|
||||
|
||||
public getInjectedTextAt(position: Position): InjectedText | null {
|
||||
const viewLineNumber = this._toValidViewLineNumber(position.lineNumber);
|
||||
const r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1);
|
||||
const lineIndex = r.index;
|
||||
const remainder = r.remainder;
|
||||
|
||||
return this.lines[lineIndex].getInjectedTextAt(remainder, position.column);
|
||||
}
|
||||
|
||||
normalizePosition(position: Position, affinity: PositionAffinity): Position {
|
||||
const viewLineNumber = this._toValidViewLineNumber(position.lineNumber);
|
||||
const r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1);
|
||||
|
@ -1101,6 +1114,10 @@ class VisibleIdentitySplitLine implements ISplitLine {
|
|||
public normalizePosition(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number, outputPosition: Position, affinity: PositionAffinity): Position {
|
||||
return outputPosition;
|
||||
}
|
||||
|
||||
public getInjectedTextAt(_outputLineIndex: number, _outputColumn: number): InjectedText | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class InvisibleIdentitySplitLine implements ISplitLine {
|
||||
|
@ -1167,6 +1184,10 @@ class InvisibleIdentitySplitLine implements ISplitLine {
|
|||
public normalizePosition(model: ISimpleModel, modelLineNumber: number, outputLineIndex: number, outputPosition: Position, affinity: PositionAffinity): Position {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
|
||||
public getInjectedTextAt(_outputLineIndex: number, _outputColumn: number): InjectedText | null {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitLine implements ISplitLine {
|
||||
|
@ -1451,6 +1472,10 @@ export class SplitLine implements ISplitLine {
|
|||
|
||||
return outputPosition;
|
||||
}
|
||||
|
||||
public getInjectedTextAt(outputLineIndex: number, outputColumn: number): InjectedText | null {
|
||||
return this._lineBreakData.getInjectedText(outputLineIndex, outputColumn - 1);
|
||||
}
|
||||
}
|
||||
|
||||
let _spaces: string[] = [''];
|
||||
|
@ -1694,6 +1719,11 @@ export class IdentityLinesCollection implements IViewModelLinesCollection {
|
|||
public getLineIndentColumn(lineNumber: number): number {
|
||||
return this.model.getLineIndentColumn(lineNumber);
|
||||
}
|
||||
|
||||
public getInjectedTextAt(position: Position): InjectedText | null {
|
||||
// Identity lines collection does not support injected text.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class OverviewRulerDecorations {
|
||||
|
|
|
@ -205,7 +205,7 @@ export class LineBreakData {
|
|||
}
|
||||
|
||||
public normalizeOffsetAroundInjections(offsetInUnwrappedLine: number, affinity: PositionAffinity): number {
|
||||
const injectedText = this.getInjectedTextAt(offsetInUnwrappedLine);
|
||||
const injectedText = this.getInjectedTextAtOffset(offsetInUnwrappedLine);
|
||||
if (!injectedText) {
|
||||
return offsetInUnwrappedLine;
|
||||
}
|
||||
|
@ -242,7 +242,18 @@ export class LineBreakData {
|
|||
return result;
|
||||
}
|
||||
|
||||
private getInjectedTextAt(offsetInUnwrappedLine: number): { injectedTextIndex: number, offsetInUnwrappedLine: number, length: number } | undefined {
|
||||
public getInjectedText(outputLineIndex: number, outputOffset: number): InjectedText | null {
|
||||
const offset = this.outputPositionToOffsetInUnwrappedLine(outputLineIndex, outputOffset);
|
||||
const injectedText = this.getInjectedTextAtOffset(offset);
|
||||
if (!injectedText) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
options: this.injectionOptions![injectedText.injectedTextIndex]
|
||||
};
|
||||
}
|
||||
|
||||
private getInjectedTextAtOffset(offsetInUnwrappedLine: number): { injectedTextIndex: number, offsetInUnwrappedLine: number, length: number } | undefined {
|
||||
const injectionOffsets = this.injectionOffsets;
|
||||
const injectionOptions = this.injectionOptions;
|
||||
|
||||
|
@ -328,6 +339,8 @@ export interface IViewModel extends ICursorSimpleModel {
|
|||
invalidateMinimapColorCache(): void;
|
||||
getValueInRange(range: Range, eol: EndOfLinePreference): string;
|
||||
|
||||
getInjectedTextAt(viewPosition: Position): InjectedText | null;
|
||||
|
||||
getModelLineMaxColumn(modelLineNumber: number): number;
|
||||
validateModelPosition(modelPosition: IPosition): Position;
|
||||
validateModelRange(range: IRange): Range;
|
||||
|
@ -372,6 +385,10 @@ export interface IViewModel extends ICursorSimpleModel {
|
|||
//#endregion
|
||||
}
|
||||
|
||||
export class InjectedText {
|
||||
constructor(public readonly options: InjectedTextOptions) { }
|
||||
}
|
||||
|
||||
export class MinimapLinesRenderingData {
|
||||
public readonly tabSize: number;
|
||||
public readonly data: Array<ViewLineData | null>;
|
||||
|
|
|
@ -21,7 +21,7 @@ import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTok
|
|||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout';
|
||||
import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollection, ILineBreaksComputerFactory } from 'vs/editor/common/viewModel/splitLinesCollection';
|
||||
import { ICoordinatesConverter, ILineBreaksComputer, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ICoordinatesConverter, InjectedText, ILineBreaksComputer, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
@ -652,6 +652,10 @@ export class ViewModel extends Disposable implements IViewModel {
|
|||
return this._decorations.getDecorationsViewportData(visibleRange).decorations;
|
||||
}
|
||||
|
||||
public getInjectedTextAt(viewPosition: Position): InjectedText | null {
|
||||
return this._lines.getInjectedTextAt(viewPosition);
|
||||
}
|
||||
|
||||
public getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData {
|
||||
let mightContainRTL = this.model.mightContainRTL();
|
||||
let mightContainNonBasicASCII = this.model.mightContainNonBasicASCII();
|
||||
|
|
Loading…
Reference in a new issue