提交 ea834640 编写于 作者: A Alex Dima

Improve mouse target detection code

上级 c687854e
......@@ -14,6 +14,7 @@ import { EditorMouseEvent } from 'vs/editor/browser/editorDom';
import * as dom from 'vs/base/browser/dom';
import * as browser from 'vs/base/browser/browser';
import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor';
import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
interface IETextRange {
boundingHeight: number;
......@@ -131,46 +132,102 @@ class MouseTarget implements IMouseTarget {
}
}
class ElementPath {
public static isTextAreaCover(path: Uint8Array): boolean {
return (
path.length === 2
&& path[0] === PartFingerprint.OverflowGuard
&& path[1] === PartFingerprint.TextAreaCover
);
}
public static isTextArea(path: Uint8Array): boolean {
return (
path.length === 2
&& path[0] === PartFingerprint.OverflowGuard
&& path[1] === PartFingerprint.TextArea
);
}
// e.g. of paths:
// - overflow-guard/monaco-scrollable-element editor-scrollable vs/lines-content/view-lines/view-line
// - overflow-guard/monaco-scrollable-element editor-scrollable vs/lines-content/view-lines/view-line/token comment js
// etc.
let REGEX = (function () {
public static isViewLines(path: Uint8Array): boolean {
return (
path.length === 4
&& path[0] === PartFingerprint.OverflowGuard
&& path[3] === PartFingerprint.ViewLines
);
}
function nodeWithClass(className: string): string {
return '[^/]*' + className + '[^/]*';
public static isChildOfViewLines(path: Uint8Array): boolean {
return (
path.length >= 4
&& path[0] === PartFingerprint.OverflowGuard
&& path[3] === PartFingerprint.ViewLines
);
}
function anyNode(): string {
return '[^/]+';
public static isCursorsLayer(path: Uint8Array): boolean {
return (
path.length === 4
&& path[0] === PartFingerprint.OverflowGuard
&& path[3] === PartFingerprint.ViewCursorsLayer
);
}
let ANCHOR = '^' + ClassNames.OVERFLOW_GUARD + '\\/';
public static isChildOfScrollableElement(path: Uint8Array): boolean {
return (
path.length >= 2
&& path[0] === PartFingerprint.OverflowGuard
&& path[1] === PartFingerprint.ScrollableElement
);
}
function createRegExp(...pieces: string[]): RegExp {
let forceEndMatch = false;
if (pieces[pieces.length - 1] === '$') {
forceEndMatch = true;
pieces.pop();
}
return new RegExp(ANCHOR + pieces.join('\\/') + (forceEndMatch ? '$' : ''));
public static isChildOfContentWidgets(path: Uint8Array): boolean {
return (
path.length >= 4
&& path[0] === PartFingerprint.OverflowGuard
&& path[3] === PartFingerprint.ContentWidgets
);
}
public static isChildOfOverflowingContentWidgets(path: Uint8Array): boolean {
return (
path.length >= 1
&& path[0] === PartFingerprint.OverflowingContentWidgets
);
}
public static isChildOfOverlayWidgets(path: Uint8Array): boolean {
return (
path.length >= 2
&& path[0] === PartFingerprint.OverflowGuard
&& path[1] === PartFingerprint.OverlayWidgets
);
}
public static isChildOfMargin(path: Uint8Array): boolean {
return (
path.length >= 2
&& path[0] === PartFingerprint.OverflowGuard
&& path[1] === PartFingerprint.Margin
);
}
return {
IS_TEXTAREA_COVER: createRegExp(nodeWithClass(ClassNames.TEXTAREA_COVER), '$'),
IS_TEXTAREA: createRegExp(ClassNames.TEXTAREA, '$'),
IS_VIEW_LINES: createRegExp(anyNode(), anyNode(), ClassNames.VIEW_LINES, '$'),
IS_CURSORS_LAYER: createRegExp(anyNode(), anyNode(), nodeWithClass(ClassNames.VIEW_CURSORS_LAYER), '$'),
IS_CHILD_OF_VIEW_LINES: createRegExp(anyNode(), anyNode(), ClassNames.VIEW_LINES),
IS_CHILD_OF_SCROLLABLE_ELEMENT: createRegExp(nodeWithClass(ClassNames.SCROLLABLE_ELEMENT)),
IS_CHILD_OF_CONTENT_WIDGETS: createRegExp(anyNode(), anyNode(), ClassNames.CONTENT_WIDGETS),
IS_CHILD_OF_OVERFLOWING_CONTENT_WIDGETS: new RegExp('^' + ClassNames.OVERFLOWING_CONTENT_WIDGETS + '\\/'),
IS_CHILD_OF_OVERLAY_WIDGETS: createRegExp(ClassNames.OVERLAY_WIDGETS),
IS_CHILD_OF_MARGIN: createRegExp(ClassNames.MARGIN),
IS_CHILD_OF_VIEW_ZONES: createRegExp(anyNode(), anyNode(), ClassNames.VIEW_ZONES),
};
})();
public static isChildOfViewZones(path: Uint8Array): boolean {
return (
path.length >= 4
&& path[0] === PartFingerprint.OverflowGuard
&& path[3] === PartFingerprint.ViewZones
);
}
public static isOverviewRuler(path: Uint8Array): boolean {
return (
path.length === 3
&& path[0] === PartFingerprint.OverflowGuard
&& path[2] === PartFingerprint.OverviewRuler
);
}
}
export class MouseTargetFactory {
......@@ -182,37 +239,17 @@ export class MouseTargetFactory {
this._viewHelper = viewHelper;
}
private getClassNamePathTo(child: Node, stopAt: Node): string {
let path: string[] = [],
className: string;
while (child && child !== document.body) {
if (child === stopAt) {
break;
}
if (child.nodeType === child.ELEMENT_NODE) {
className = (<HTMLElement>child).className;
if (className) {
path.unshift(className);
}
}
child = child.parentNode;
}
return path.join('/');
}
public mouseTargetIsWidget(e: EditorMouseEvent): boolean {
let t = <Element>e.target;
let path = this.getClassNamePathTo(t, this._viewHelper.viewDomNode);
let path = PartFingerprints.collect(t, this._viewHelper.viewDomNode);
// Is it a content widget?
if (REGEX.IS_CHILD_OF_CONTENT_WIDGETS.test(path) || REGEX.IS_CHILD_OF_OVERFLOWING_CONTENT_WIDGETS.test(path)) {
if (ElementPath.isChildOfContentWidgets(path) || ElementPath.isChildOfOverflowingContentWidgets(path)) {
return true;
}
// Is it an overlay widget?
if (REGEX.IS_CHILD_OF_OVERLAY_WIDGETS.test(path)) {
if (ElementPath.isChildOfOverlayWidgets(path)) {
return true;
}
......@@ -232,6 +269,7 @@ export class MouseTargetFactory {
let mouseVerticalOffset = Math.max(0, this._viewHelper.getScrollTop() + (e.posy - e.editorPos.top));
let mouseContentHorizontalOffset = this._viewHelper.getScrollLeft() + (e.posx - e.editorPos.left) - layoutInfo.contentLeft;
let mouseColumn = this._getMouseColumn(mouseContentHorizontalOffset);
// console.log(`mouseVerticalOffset: ${mouseVerticalOffset}, mouseContentHorizontalOffset: ${mouseContentHorizontalOffset}, mouseColumn: ${mouseColumn}`)
let t = <Element>e.target;
......@@ -252,15 +290,15 @@ export class MouseTargetFactory {
}
}
let path = this.getClassNamePathTo(t, this._viewHelper.viewDomNode);
let path = PartFingerprints.collect(t, this._viewHelper.viewDomNode);
// Is it a content widget?
if (REGEX.IS_CHILD_OF_CONTENT_WIDGETS.test(path) || REGEX.IS_CHILD_OF_OVERFLOWING_CONTENT_WIDGETS.test(path)) {
if (ElementPath.isChildOfContentWidgets(path) || ElementPath.isChildOfOverflowingContentWidgets(path)) {
return this.createMouseTargetFromContentWidgetsChild(t, mouseColumn);
}
// Is it an overlay widget?
if (REGEX.IS_CHILD_OF_OVERLAY_WIDGETS.test(path)) {
if (ElementPath.isChildOfOverlayWidgets(path)) {
return this.createMouseTargetFromOverlayWidgetsChild(t, mouseColumn);
}
......@@ -272,7 +310,7 @@ export class MouseTargetFactory {
}
// Is it the textarea cover?
if (REGEX.IS_TEXTAREA_COVER.test(path)) {
if (ElementPath.isTextAreaCover(path)) {
if (this._context.configuration.editor.viewInfo.glyphMargin) {
return this.createMouseTargetFromGlyphMargin(t, mouseVerticalOffset, mouseColumn);
} else if (this._context.configuration.editor.viewInfo.renderLineNumbers) {
......@@ -283,12 +321,12 @@ export class MouseTargetFactory {
}
// Is it the textarea?
if (REGEX.IS_TEXTAREA.test(path)) {
if (ElementPath.isTextArea(path)) {
return new MouseTarget(t, MouseTargetType.TEXTAREA);
}
// Is it a view zone?
if (REGEX.IS_CHILD_OF_VIEW_ZONES.test(path)) {
if (ElementPath.isChildOfViewZones(path)) {
// Check if it is at a view zone
let viewZoneData = this._getZoneAtCoord(mouseVerticalOffset);
if (viewZoneData) {
......@@ -298,7 +336,7 @@ export class MouseTargetFactory {
}
// Is it the view lines container?
if (REGEX.IS_VIEW_LINES.test(path)) {
if (ElementPath.isViewLines(path)) {
// Sometimes, IE returns this target when right clicking on top of text
// -> See Bug #12990: [F12] Context menu shows incorrect position while doing a resize
......@@ -324,13 +362,13 @@ export class MouseTargetFactory {
}
// Is it a child of the view lines container?
if (!testEventTarget || REGEX.IS_CHILD_OF_VIEW_LINES.test(path)) {
if (!testEventTarget || ElementPath.isChildOfViewLines(path)) {
let hitTestResult = this._doHitTest(e, mouseVerticalOffset);
if (hitTestResult.position) {
return this.createMouseTargetFromHitTestPosition(t, hitTestResult.position.lineNumber, hitTestResult.position.column, mouseContentHorizontalOffset, mouseColumn);
} else if (hitTestResult.hitTarget) {
t = hitTestResult.hitTarget;
path = this.getClassNamePathTo(t, this._viewHelper.viewDomNode);
path = PartFingerprints.collect(t, this._viewHelper.viewDomNode);
// TODO@Alex: try again with this different target, but guard against recursion.
// Is it a cursor ?
......@@ -348,16 +386,16 @@ export class MouseTargetFactory {
}
// Is it the cursors layer?
if (REGEX.IS_CURSORS_LAYER.test(path)) {
if (ElementPath.isCursorsLayer(path)) {
return new MouseTarget(t, MouseTargetType.UNKNOWN);
}
// Is it a child of the scrollable element?
if (REGEX.IS_CHILD_OF_SCROLLABLE_ELEMENT.test(path)) {
if (ElementPath.isChildOfScrollableElement(path)) {
return this.createMouseTargetFromScrollbar(t, mouseVerticalOffset, mouseColumn);
}
if (REGEX.IS_CHILD_OF_MARGIN.test(path)) {
if (ElementPath.isChildOfMargin(path)) {
let offset = Math.abs(e.posx - e.editorPos.left);
if (offset <= layoutInfo.glyphMarginWidth) {
......@@ -376,7 +414,7 @@ export class MouseTargetFactory {
return this.createMouseTargetFromLinesDecorationsChild(t, mouseVerticalOffset, mouseColumn);
}
if (/OverviewRuler/i.test(path)) {
if (ElementPath.isOverviewRuler(path)) {
return this.createMouseTargetFromScrollbar(t, mouseVerticalOffset, mouseColumn);
}
......
......@@ -42,7 +42,7 @@ import { ScrollDecorationViewPart } from 'vs/editor/browser/viewParts/scrollDeco
import { SelectionsOverlay } from 'vs/editor/browser/viewParts/selections/selections';
import { ViewCursors } from 'vs/editor/browser/viewParts/viewCursors/viewCursors';
import { ViewZones } from 'vs/editor/browser/viewParts/viewZones/viewZones';
import { ViewPart } from 'vs/editor/browser/view/viewPart';
import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
import { ViewContext, IViewEventHandler } from 'vs/editor/common/view/viewContext';
import { IViewModel } from 'vs/editor/common/viewModel/viewModel';
import { ViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
......@@ -118,6 +118,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
this.domNode.className = configuration.editor.viewInfo.editorClassName;
this.overflowGuardContainer = document.createElement('div');
PartFingerprints.write(this.overflowGuardContainer, PartFingerprint.OverflowGuard);
this.overflowGuardContainer.className = editorBrowser.ClassNames.OVERFLOW_GUARD;
// The layout provider has such responsibilities as:
......@@ -174,6 +175,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
private createTextArea(): void {
// Text Area (The focus will always be in the textarea when the cursor is blinking)
this.textArea = <HTMLTextAreaElement>document.createElement('textarea');
PartFingerprints.write(this.textArea, PartFingerprint.TextArea);
this.textArea.className = editorBrowser.ClassNames.TEXTAREA;
this.textArea.setAttribute('wrap', 'off');
this.textArea.setAttribute('autocorrect', 'off');
......@@ -195,6 +197,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
// (there have been reports of tiny blinking cursors)
// (in WebKit the textarea is 1px by 1px because it cannot handle input to a 0x0 textarea)
this.textAreaCover = document.createElement('div');
PartFingerprints.write(this.textAreaCover, PartFingerprint.TextAreaCover);
if (this._context.configuration.editor.viewInfo.glyphMargin) {
this.textAreaCover.className = 'monaco-editor-background ' + editorBrowser.ClassNames.GLYPH_MARGIN + ' ' + editorBrowser.ClassNames.TEXTAREA_COVER;
} else {
......
......@@ -26,3 +26,54 @@ export abstract class ViewPart extends ViewEventHandler {
public abstract prepareRender(ctx: IRenderingContext): void;
public abstract render(ctx: IRestrictedRenderingContext): void;
}
export const enum PartFingerprint {
None,
ContentWidgets,
Margin,
OverflowingContentWidgets,
OverflowGuard,
OverlayWidgets,
OverviewRuler,
ScrollableElement,
TextArea,
TextAreaCover,
ViewCursorsLayer,
ViewLines,
ViewZones
}
export class PartFingerprints {
public static write(target: Element, partId: PartFingerprint) {
target.setAttribute('data-mprt', String(partId));
}
public static read(target: Element): PartFingerprint {
let r = target.getAttribute('data-mprt');
if (r === null) {
return PartFingerprint.None;
}
return parseInt(r, 10);
}
public static collect(child: Element, stopAt: Element): Uint8Array {
let result: PartFingerprint[] = [], resultLen = 0;
while (child && child !== document.body) {
if (child === stopAt) {
break;
}
if (child.nodeType === child.ELEMENT_NODE) {
result[resultLen++] = this.read(child);
}
child = child.parentElement;
}
let r = new Uint8Array(resultLen);
for (let i = 0; i < resultLen; i++) {
r[i] = result[resultLen - i - 1];
}
return r;
}
}
......@@ -11,6 +11,7 @@ import { IOverviewRulerLayoutInfo, ScrollableElement } from 'vs/base/browser/ui/
import { EventType, IConfiguration, IConfigurationChangedEvent, IScrollEvent, INewScrollPosition } from 'vs/editor/common/editorCommon';
import { ClassNames } from 'vs/editor/browser/editorBrowser';
import { IViewEventBus } from 'vs/editor/common/view/viewContext';
import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
function addPropertyIfPresent(src: any, dst: any, prop: string): void {
if (src.hasOwnProperty(prop)) {
......@@ -56,6 +57,8 @@ export class ScrollManager implements IDisposable {
addPropertyIfPresent(configScrollbarOpts, scrollbarOptions, 'mouseWheelScrollSensitivity');
this.scrollbar = new ScrollableElement(linesContent, scrollbarOptions);
PartFingerprints.write(this.scrollbar.getDomNode(), PartFingerprint.ScrollableElement);
this.onLayoutInfoChanged();
this.toDispose.push(this.scrollbar);
this.toDispose.push(this.scrollbar.onScroll((e: IScrollEvent) => {
......
......@@ -9,7 +9,7 @@ import * as dom from 'vs/base/browser/dom';
import { StyleMutator } from 'vs/base/browser/styleMutator';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ClassNames, ContentWidgetPositionPreference, IContentWidget } from 'vs/editor/browser/editorBrowser';
import { ViewPart } from 'vs/editor/browser/view/viewPart';
import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
import { Position } from 'vs/editor/common/core/position';
......@@ -66,11 +66,13 @@ export class ViewContentWidgets extends ViewPart {
this._renderData = {};
this.domNode = document.createElement('div');
PartFingerprints.write(this.domNode, PartFingerprint.ContentWidgets);
this.domNode.className = ClassNames.CONTENT_WIDGETS;
this.domNode.style.position = 'absolute';
this.domNode.style.top = '0';
this.overflowingContentWidgetsDomNode = document.createElement('div');
PartFingerprints.write(this.overflowingContentWidgetsDomNode, PartFingerprint.OverflowingContentWidgets);
this.overflowingContentWidgetsDomNode.className = ClassNames.OVERFLOWING_CONTENT_WIDGETS;
}
......
......@@ -18,6 +18,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext';
import { ViewLinesViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
import { VisibleRange, LineVisibleRanges } from 'vs/editor/common/view/renderingContext';
import { ILayoutProvider } from 'vs/editor/browser/viewLayout/layoutProvider';
import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
class LastRenderedData {
......@@ -80,6 +81,8 @@ export class ViewLines extends ViewLayer<ViewLine> {
this._revealHorizontalRightPadding = this._context.configuration.editor.viewInfo.revealHorizontalRightPadding;
this._canUseTranslate3d = context.configuration.editor.viewInfo.canUseTranslate3d;
this._layoutProvider = layoutProvider;
PartFingerprints.write(this.domNode.domNode, PartFingerprint.ViewLines);
this.domNode.setClassName(ClassNames.VIEW_LINES);
Configuration.applyFontInfo(this.domNode, this._context.configuration.editor.fontInfo);
......
......@@ -8,7 +8,7 @@
import { StyleMutator, FastDomNode, createFastDomNode } from 'vs/base/browser/styleMutator';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ClassNames } from 'vs/editor/browser/editorBrowser';
import { ViewPart } from 'vs/editor/browser/view/viewPart';
import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
import { ILayoutProvider } from 'vs/editor/browser/viewLayout/layoutProvider';
......@@ -39,6 +39,7 @@ export class Margin extends ViewPart {
public _createDomNode(): HTMLElement {
let domNode = document.createElement('div');
PartFingerprints.write(domNode, PartFingerprint.Margin);
domNode.className = ClassNames.MARGIN + ' monaco-editor-background';
domNode.style.position = 'absolute';
domNode.setAttribute('role', 'presentation');
......
......@@ -9,7 +9,7 @@ import 'vs/css!./overlayWidgets';
import { StyleMutator } from 'vs/base/browser/styleMutator';
import { EditorLayoutInfo } from 'vs/editor/common/editorCommon';
import { ClassNames, IOverlayWidget, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
import { ViewPart } from 'vs/editor/browser/view/viewPart';
import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
......@@ -42,6 +42,7 @@ export class ViewOverlayWidgets extends ViewPart {
this._editorWidth = 0;
this.domNode = document.createElement('div');
PartFingerprints.write(this.domNode, PartFingerprint.OverlayWidgets);
this.domNode.className = ClassNames.OVERLAY_WIDGETS;
}
......
......@@ -9,6 +9,7 @@ import { OverviewRulerPosition, OverviewRulerLane, OverviewRulerZone, ColorZone
import { IDisposable } from 'vs/base/common/lifecycle';
import * as browser from 'vs/base/browser/browser';
import { OverviewZoneManager } from 'vs/editor/common/view/overviewZoneManager';
import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
export class OverviewRulerImpl {
......@@ -24,6 +25,8 @@ export class OverviewRulerImpl {
this._canvasLeftOffset = canvasLeftOffset;
this._domNode = <HTMLCanvasElement>document.createElement('canvas');
PartFingerprints.write(this._domNode, PartFingerprint.OverviewRuler);
this._domNode.className = cssClassName;
this._domNode.style.position = 'absolute';
......
......@@ -8,7 +8,7 @@
import 'vs/css!./viewCursors';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ClassNames } from 'vs/editor/browser/editorBrowser';
import { ViewPart } from 'vs/editor/browser/view/viewPart';
import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
import { Position } from 'vs/editor/common/core/position';
import { IViewCursorRenderData, ViewCursor } from 'vs/editor/browser/viewParts/viewCursors/viewCursor';
import { ViewContext } from 'vs/editor/common/view/viewContext';
......@@ -49,6 +49,7 @@ export class ViewCursors extends ViewPart {
this._renderData = [];
this._domNode = createFastDomNode(document.createElement('div'));
PartFingerprints.write(this._domNode.domNode, PartFingerprint.ViewCursorsLayer);
this._updateDomClassName();
this._domNode.domNode.appendChild(this._primaryCursor.getDomNode());
......
......@@ -8,7 +8,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { StyleMutator } from 'vs/base/browser/styleMutator';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ClassNames, IViewZone } from 'vs/editor/browser/editorBrowser';
import { ViewPart } from 'vs/editor/browser/view/viewPart';
import { ViewPart, PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart';
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { Position } from 'vs/editor/common/core/position';
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
......@@ -49,6 +49,7 @@ export class ViewZones extends ViewPart {
this._whitespaceManager = whitespaceManager;
this.domNode = document.createElement('div');
PartFingerprints.write(this.domNode, PartFingerprint.ViewZones);
this.domNode.className = ClassNames.VIEW_ZONES;
this.domNode.style.position = 'absolute';
this.domNode.setAttribute('role', 'presentation');
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册