Track changes to hideUnderline

This commit is contained in:
Daniel Imms
2020-04-18 06:18:01 -07:00
parent ccf869e671
commit f2ed01993d
8 changed files with 73 additions and 39 deletions
@@ -16,11 +16,11 @@ export class LinkRenderLayer extends BaseRenderLayer {
constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) { constructor(container: HTMLElement, zIndex: number, colors: IColorSet, terminal: ILinkifierAccessor) {
super(container, 'link', zIndex, true, colors); super(container, 'link', zIndex, true, colors);
terminal.linkifier.onLinkHover(e => this._onLinkHover(e)); terminal.linkifier.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
terminal.linkifier.onLinkLeave(e => this._onLinkLeave(e)); terminal.linkifier.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
terminal.linkifier2.onLinkHover(e => this._onLinkHover(e)); terminal.linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
terminal.linkifier2.onLinkLeave(e => this._onLinkLeave(e)); terminal.linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
} }
public resize(terminal: Terminal, dim: IRenderDimensions): void { public resize(terminal: Terminal, dim: IRenderDimensions): void {
@@ -45,7 +45,7 @@ export class LinkRenderLayer extends BaseRenderLayer {
} }
} }
private _onLinkHover(e: ILinkifierEvent): void { private _onShowLinkUnderline(e: ILinkifierEvent): void {
if (e.fg === INVERTED_DEFAULT_COLOR) { if (e.fg === INVERTED_DEFAULT_COLOR) {
this._ctx.fillStyle = this._colors.background.css; this._ctx.fillStyle = this._colors.background.css;
} else if (e.fg !== undefined && is256Color(e.fg)) { } else if (e.fg !== undefined && is256Color(e.fg)) {
@@ -69,7 +69,7 @@ export class LinkRenderLayer extends BaseRenderLayer {
this._state = e; this._state = e;
} }
private _onLinkLeave(e: ILinkifierEvent): void { private _onHideLinkUnderline(e: ILinkifierEvent): void {
this._clearCurrentLink(); this._clearCurrentLink();
} }
} }
+6 -6
View File
@@ -35,10 +35,10 @@ export class Linkifier implements ILinkifier {
private _nextLinkMatcherId = 0; private _nextLinkMatcherId = 0;
private _rowsToLinkify: { start: number | undefined, end: number | undefined }; private _rowsToLinkify: { start: number | undefined, end: number | undefined };
private _onLinkHover = new EventEmitter<ILinkifierEvent>(); private _onShowLinkUnderline = new EventEmitter<ILinkifierEvent>();
public get onLinkHover(): IEvent<ILinkifierEvent> { return this._onLinkHover.event; } public get onShowLinkUnderline(): IEvent<ILinkifierEvent> { return this._onShowLinkUnderline.event; }
private _onLinkLeave = new EventEmitter<ILinkifierEvent>(); private _onHideLinkUnderline = new EventEmitter<ILinkifierEvent>();
public get onLinkLeave(): IEvent<ILinkifierEvent> { return this._onLinkLeave.event; } public get onHideLinkUnderline(): IEvent<ILinkifierEvent> { return this._onHideLinkUnderline.event; }
private _onLinkTooltip = new EventEmitter<ILinkifierEvent>(); private _onLinkTooltip = new EventEmitter<ILinkifierEvent>();
public get onLinkTooltip(): IEvent<ILinkifierEvent> { return this._onLinkTooltip.event; } public get onLinkTooltip(): IEvent<ILinkifierEvent> { return this._onLinkTooltip.event; }
@@ -307,7 +307,7 @@ export class Linkifier implements ILinkifier {
} }
}, },
() => { () => {
this._onLinkHover.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); this._onShowLinkUnderline.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));
this._element!.classList.add('xterm-cursor-pointer'); this._element!.classList.add('xterm-cursor-pointer');
}, },
e => { e => {
@@ -319,7 +319,7 @@ export class Linkifier implements ILinkifier {
} }
}, },
() => { () => {
this._onLinkLeave.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg)); this._onHideLinkUnderline.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));
this._element!.classList.remove('xterm-cursor-pointer'); this._element!.classList.remove('xterm-cursor-pointer');
if (matcher.hoverLeaveCallback) { if (matcher.hoverLeaveCallback) {
matcher.hoverLeaveCallback(); matcher.hoverLeaveCallback();
+2 -2
View File
@@ -48,7 +48,7 @@ describe('Linkifier2', () => {
}; };
it('onLinkHover event range is correct', done => { it('onLinkHover event range is correct', done => {
linkifier.onLinkHover(e => { linkifier.onShowLinkUnderline(e => {
assert.equal(link.range.start.x - 1, e.x1); assert.equal(link.range.start.x - 1, e.x1);
assert.equal(link.range.start.y - 1, e.y1); assert.equal(link.range.start.y - 1, e.y1);
assert.equal(link.range.end.x, e.x2); assert.equal(link.range.end.x, e.x2);
@@ -61,7 +61,7 @@ describe('Linkifier2', () => {
}); });
it('onLinkLeave event range is correct', done => { it('onLinkLeave event range is correct', done => {
linkifier.onLinkLeave(e => { linkifier.onHideLinkUnderline(e => {
assert.equal(link.range.start.x - 1, e.x1); assert.equal(link.range.start.x - 1, e.x1);
assert.equal(link.range.start.y - 1, e.y1); assert.equal(link.range.start.y - 1, e.y1);
assert.equal(link.range.end.x, e.x2); assert.equal(link.range.end.x, e.x2);
+43 -10
View File
@@ -9,20 +9,25 @@ import { IMouseService, IRenderService } from './services/Services';
import { IBufferService } from 'common/services/Services'; import { IBufferService } from 'common/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter'; import { EventEmitter, IEvent } from 'common/EventEmitter';
interface ILinkState {
isHovered: boolean;
}
export class Linkifier2 implements ILinkifier2 { export class Linkifier2 implements ILinkifier2 {
private _element: HTMLElement | undefined; private _element: HTMLElement | undefined;
private _mouseService: IMouseService | undefined; private _mouseService: IMouseService | undefined;
private _renderService: IRenderService | undefined; private _renderService: IRenderService | undefined;
private _linkProviders: ILinkProvider[] = []; private _linkProviders: ILinkProvider[] = [];
private _currentLink: ILink | undefined; private _currentLink: ILink | undefined;
private _currentLinkState: ILinkState | undefined;
private _lastMouseEvent: MouseEvent | undefined; private _lastMouseEvent: MouseEvent | undefined;
private _linkCacheDisposables: IDisposable[] = []; private _linkCacheDisposables: IDisposable[] = [];
private _lastBufferCell: IBufferCellPosition | undefined; private _lastBufferCell: IBufferCellPosition | undefined;
private _onLinkHover = new EventEmitter<ILinkifierEvent>(); private _onShowLinkUnderline = new EventEmitter<ILinkifierEvent>();
public get onLinkHover(): IEvent<ILinkifierEvent> { return this._onLinkHover.event; } public get onShowLinkUnderline(): IEvent<ILinkifierEvent> { return this._onShowLinkUnderline.event; }
private _onLinkLeave = new EventEmitter<ILinkifierEvent>(); private _onHideLinkUnderline = new EventEmitter<ILinkifierEvent>();
public get onLinkLeave(): IEvent<ILinkifierEvent> { return this._onLinkLeave.event; } public get onHideLinkUnderline(): IEvent<ILinkifierEvent> { return this._onHideLinkUnderline.event; }
constructor( constructor(
private readonly _bufferService: IBufferService private readonly _bufferService: IBufferService
@@ -151,6 +156,7 @@ export class Linkifier2 implements ILinkifier2 {
if (!startRow || !endRow || (this._currentLink.range.start.y >= startRow && this._currentLink.range.end.y <= endRow)) { if (!startRow || !endRow || (this._currentLink.range.start.y >= startRow && this._currentLink.range.end.y <= endRow)) {
this._linkLeave(this._element, this._currentLink, this._lastMouseEvent); this._linkLeave(this._element, this._currentLink, this._lastMouseEvent);
this._currentLink = undefined; this._currentLink = undefined;
this._currentLinkState = undefined;
this._linkCacheDisposables.forEach(l => l.dispose()); this._linkCacheDisposables.forEach(l => l.dispose());
this._linkCacheDisposables = []; this._linkCacheDisposables = [];
} }
@@ -170,8 +176,25 @@ export class Linkifier2 implements ILinkifier2 {
// Trigger hover if the we have a link at the position // Trigger hover if the we have a link at the position
if (this._linkAtPosition(link, position)) { if (this._linkAtPosition(link, position)) {
this._currentLink = link; this._currentLink = link;
this._currentLinkState = { isHovered: true };
this._linkHover(this._element, link, this._lastMouseEvent); this._linkHover(this._element, link, this._lastMouseEvent);
// Add listener for tracking hideUnderline changes
let hideUnderlineValue = link.hideUnderline || false;
Object.defineProperties(link, {
hideUnderline: {
get: () => hideUnderlineValue,
set: v => {
if (hideUnderlineValue !== v) {
hideUnderlineValue = v;
if (this._currentLinkState?.isHovered) {
this._fireUnderlineEvent(link, !v);
}
}
}
}
});
// Add listener for rerendering // Add listener for rerendering
if (this._renderService) { if (this._renderService) {
this._linkCacheDisposables.push(this._renderService.onRenderedBufferChange(e => { this._linkCacheDisposables.push(this._renderService.onRenderedBufferChange(e => {
@@ -182,11 +205,11 @@ export class Linkifier2 implements ILinkifier2 {
} }
protected _linkHover(element: HTMLElement, link: ILink, event: MouseEvent): void { protected _linkHover(element: HTMLElement, link: ILink, event: MouseEvent): void {
const range = link.range;
const scrollOffset = this._bufferService.buffer.ydisp;
if (!link.hideUnderline) { if (!link.hideUnderline) {
this._onLinkHover.fire(this._createLinkHoverEvent(range.start.x - 1, range.start.y - scrollOffset - 1, range.end.x, range.end.y - scrollOffset - 1, undefined)); this._fireUnderlineEvent(link, true);
}
if (this._currentLinkState) {
this._currentLinkState.isHovered = true;
} }
element.classList.add('xterm-cursor-pointer'); element.classList.add('xterm-cursor-pointer');
@@ -195,11 +218,21 @@ export class Linkifier2 implements ILinkifier2 {
} }
} }
protected _linkLeave(element: HTMLElement, link: ILink, event: MouseEvent): void { private _fireUnderlineEvent(link: ILink, showEvent: boolean): void {
const range = link.range; const range = link.range;
const scrollOffset = this._bufferService.buffer.ydisp; const scrollOffset = this._bufferService.buffer.ydisp;
const event = this._createLinkHoverEvent(range.start.x - 1, range.start.y - scrollOffset - 1, range.end.x, range.end.y - scrollOffset - 1, undefined);
const emitter = showEvent ? this._onShowLinkUnderline : this._onHideLinkUnderline;
emitter.fire(event);
}
this._onLinkLeave.fire(this._createLinkHoverEvent(range.start.x - 1, range.start.y - scrollOffset - 1, range.end.x, range.end.y - scrollOffset - 1, undefined)); protected _linkLeave(element: HTMLElement, link: ILink, event: MouseEvent): void {
if (!link.hideUnderline) {
this._fireUnderlineEvent(link, false);
}
if (this._currentLinkState) {
this._currentLinkState.isHovered = false;
}
element.classList.remove('xterm-cursor-pointer'); element.classList.remove('xterm-cursor-pointer');
if (link.leave) { if (link.leave) {
+4 -4
View File
@@ -96,8 +96,8 @@ export interface ILinkifierEvent {
} }
export interface ILinkifier { export interface ILinkifier {
onLinkHover: IEvent<ILinkifierEvent>; onShowLinkUnderline: IEvent<ILinkifierEvent>;
onLinkLeave: IEvent<ILinkifierEvent>; onHideLinkUnderline: IEvent<ILinkifierEvent>;
onLinkTooltip: IEvent<ILinkifierEvent>; onLinkTooltip: IEvent<ILinkifierEvent>;
attachToDom(element: HTMLElement, mouseZoneManager: IMouseZoneManager): void; attachToDom(element: HTMLElement, mouseZoneManager: IMouseZoneManager): void;
@@ -107,8 +107,8 @@ export interface ILinkifier {
} }
export interface ILinkifier2 { export interface ILinkifier2 {
onLinkHover: IEvent<ILinkifierEvent>; onShowLinkUnderline: IEvent<ILinkifierEvent>;
onLinkLeave: IEvent<ILinkifierEvent>; onHideLinkUnderline: IEvent<ILinkifierEvent>;
attachToDom(element: HTMLElement, mouseService: IMouseService, renderService: IRenderService): void; attachToDom(element: HTMLElement, mouseService: IMouseService, renderService: IRenderService): void;
registerLinkProvider(linkProvider: ILinkProvider): IDisposable; registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
+6 -6
View File
@@ -24,11 +24,11 @@ export class LinkRenderLayer extends BaseRenderLayer {
optionsService: IOptionsService optionsService: IOptionsService
) { ) {
super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService); super(container, 'link', zIndex, true, colors, rendererId, bufferService, optionsService);
linkifier.onLinkHover(e => this._onLinkHover(e)); linkifier.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
linkifier.onLinkLeave(e => this._onLinkLeave(e)); linkifier.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
linkifier2.onLinkHover(e => this._onLinkHover(e)); linkifier2.onShowLinkUnderline(e => this._onShowLinkUnderline(e));
linkifier2.onLinkLeave(e => this._onLinkLeave(e)); linkifier2.onHideLinkUnderline(e => this._onHideLinkUnderline(e));
} }
public resize(dim: IRenderDimensions): void { public resize(dim: IRenderDimensions): void {
@@ -53,7 +53,7 @@ export class LinkRenderLayer extends BaseRenderLayer {
} }
} }
private _onLinkHover(e: ILinkifierEvent): void { private _onShowLinkUnderline(e: ILinkifierEvent): void {
if (e.fg === INVERTED_DEFAULT_COLOR) { if (e.fg === INVERTED_DEFAULT_COLOR) {
this._ctx.fillStyle = this._colors.background.css; this._ctx.fillStyle = this._colors.background.css;
} else if (e.fg && is256Color(e.fg)) { } else if (e.fg && is256Color(e.fg)) {
@@ -77,7 +77,7 @@ export class LinkRenderLayer extends BaseRenderLayer {
this._state = e; this._state = e;
} }
private _onLinkLeave(e: ILinkifierEvent): void { private _onHideLinkUnderline(e: ILinkifierEvent): void {
this._clearCurrentLink(); this._clearCurrentLink();
} }
} }
+4 -4
View File
@@ -86,11 +86,11 @@ export class DomRenderer extends Disposable implements IRenderer {
this._screenElement.appendChild(this._rowContainer); this._screenElement.appendChild(this._rowContainer);
this._screenElement.appendChild(this._selectionContainer); this._screenElement.appendChild(this._selectionContainer);
this._linkifier.onLinkHover(e => this._onLinkHover(e)); this._linkifier.onShowLinkUnderline(e => this._onLinkHover(e));
this._linkifier.onLinkLeave(e => this._onLinkLeave(e)); this._linkifier.onHideLinkUnderline(e => this._onLinkLeave(e));
this._linkifier2.onLinkHover(e => this._onLinkHover(e)); this._linkifier2.onShowLinkUnderline(e => this._onLinkHover(e));
this._linkifier2.onLinkLeave(e => this._onLinkLeave(e)); this._linkifier2.onHideLinkUnderline(e => this._onLinkLeave(e));
} }
public dispose(): void { public dispose(): void {
+2 -1
View File
@@ -1121,7 +1121,8 @@ declare module 'xterm' {
text: string; text: string;
/** /**
* Whether to hide the link's underline. * Whether to hide the underline, this property is tracked and changes after the link is
* provided will trigger changes.
*/ */
hideUnderline?: boolean; hideUnderline?: boolean;