From eb961878012d6ef3c411406e64987061a67d3ca7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 16 May 2022 10:08:51 -0700 Subject: [PATCH] Support selectionForeground in dom Part of #3810 --- src/browser/TestUtils.test.ts | 52 +++++++++++++++++++ src/browser/renderer/dom/DomRenderer.ts | 2 + .../dom/DomRendererRowFactory.test.ts | 8 +-- .../renderer/dom/DomRendererRowFactory.ts | 16 ++++-- src/browser/services/SelectionService.ts | 9 ++++ src/browser/services/Services.ts | 1 + 6 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/browser/TestUtils.test.ts b/src/browser/TestUtils.test.ts index 85e2bb55..f7e6bcb4 100644 --- a/src/browser/TestUtils.test.ts +++ b/src/browser/TestUtils.test.ts @@ -16,6 +16,7 @@ import { Terminal } from 'browser/Terminal'; import { IUnicodeService, IOptionsService, ICoreService, ICoreMouseService } from 'common/services/Services'; import { IFunctionIdentifier, IParams } from 'common/parser/Types'; import { AttributeData } from 'common/buffer/AttributeData'; +import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types'; export class TestTerminal extends Terminal { public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; } @@ -449,3 +450,54 @@ export class MockCharacterJoinerService implements ICharacterJoinerService { return []; } } + +export class MockSelectionService implements ISelectionService { + public serviceBrand: undefined; + public selectionText: string = ''; + public hasSelection: boolean = false; + public selectionStart: [number, number] | undefined; + public selectionEnd: [number, number] | undefined; + public onLinuxMouseSelection = new EventEmitter().event; + public onRequestRedraw = new EventEmitter().event; + public onRequestScrollLines = new EventEmitter().event; + public onSelectionChange = new EventEmitter().event; + public disable(): void { + throw new Error('Method not implemented.'); + } + public enable(): void { + throw new Error('Method not implemented.'); + } + public reset(): void { + throw new Error('Method not implemented.'); + } + public setSelection(row: number, col: number, length: number): void { + throw new Error('Method not implemented.'); + } + public selectAll(): void { + throw new Error('Method not implemented.'); + } + public selectLines(start: number, end: number): void { + throw new Error('Method not implemented.'); + } + public clearSelection(): void { + throw new Error('Method not implemented.'); + } + public rightClickSelect(event: MouseEvent): void { + throw new Error('Method not implemented.'); + } + public shouldColumnSelect(event: MouseEvent | KeyboardEvent): boolean { + throw new Error('Method not implemented.'); + } + public shouldForceSelection(event: MouseEvent): boolean { + throw new Error('Method not implemented.'); + } + public refresh(isLinuxMouseSelection?: boolean): void { + throw new Error('Method not implemented.'); + } + public onMouseDown(event: MouseEvent): void { + throw new Error('Method not implemented.'); + } + public isCellInSelection(x: number, y: number): boolean { + return false; + } +} diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index d15d7eac..840ef40d 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -281,6 +281,8 @@ export class DomRenderer extends Disposable implements IRenderer { this._selectionContainer.removeChild(this._selectionContainer.children[0]); } + this.renderRows(0, this._bufferService.rows - 1); + // Selection does not exist if (!start || !end) { return; diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index bb511a47..ae7a1434 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -10,9 +10,9 @@ import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, DEFAULT_ATTR, FgFlags, import { BufferLine, DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { IBufferLine } from 'common/Types'; import { CellData } from 'common/buffer/CellData'; -import { MockCoreService, MockDecorationService, MockOptionsService } from 'common/TestUtils.test'; +import { MockBufferService, MockCoreService, MockDecorationService, MockOptionsService } from 'common/TestUtils.test'; import { css } from 'common/Color'; -import { MockCharacterJoinerService } from 'browser/TestUtils.test'; +import { MockCharacterJoinerService, MockSelectionService } from 'browser/TestUtils.test'; describe('DomRendererRowFactory', () => { let dom: jsdom.JSDOM; @@ -50,7 +50,9 @@ describe('DomRendererRowFactory', () => { new MockCharacterJoinerService(), new MockOptionsService({ drawBoldTextInBrightColors: true }), new MockCoreService(), - new MockDecorationService() + new MockDecorationService(), + new MockBufferService(80, 30), + new MockSelectionService() ); lineData = createEmptyLineData(2); }); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 4bed48e5..f421859b 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -7,10 +7,10 @@ import { IBufferLine, ICellData, IColor } from 'common/Types'; import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants'; import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Attributes } from 'common/buffer/Constants'; import { CellData } from 'common/buffer/CellData'; -import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; +import { IBufferService, ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { color, rgba } from 'common/Color'; import { IColorSet } from 'browser/Types'; -import { ICharacterJoinerService } from 'browser/services/Services'; +import { ICharacterJoinerService, ISelectionService } from 'browser/services/Services'; import { JoinedCellData } from 'browser/services/CharacterJoinerService'; import { isPowerlineGlyph } from 'browser/renderer/RendererUtils'; @@ -34,7 +34,9 @@ export class DomRendererRowFactory { @ICharacterJoinerService private readonly _characterJoinerService: ICharacterJoinerService, @IOptionsService private readonly _optionsService: IOptionsService, @ICoreService private readonly _coreService: ICoreService, - @IDecorationService private readonly _decorationService: IDecorationService + @IDecorationService private readonly _decorationService: IDecorationService, + @IBufferService private readonly _bufferService: IBufferService, + @ISelectionService private readonly _selectionService: ISelectionService ) { } @@ -195,6 +197,14 @@ export class DomRendererRowFactory { isTop = d.options.layer === 'top'; } + // Apply selection foreground if applicable + if (!isTop) { + if (this._colors.selectionForeground && this._selectionService.isCellInSelection(x, row)) { + fg = this._colors.selectionForeground.rgba >> 8 & 0xFFFFFF; + fgOverride = this._colors.selectionForeground; + } + } + // If it's a top decoration, render above the selection if (isTop) { charElement.classList.add(`xterm-decoration-top`); diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts index 4b882e6d..c7b7707a 100644 --- a/src/browser/services/SelectionService.ts +++ b/src/browser/services/SelectionService.ts @@ -308,6 +308,15 @@ export class SelectionService extends Disposable implements ISelectionService { return this._areCoordsInSelection(coords, start, end); } + public isCellInSelection(x: number, y: number): boolean { + const start = this._model.finalSelectionStart; + const end = this._model.finalSelectionEnd; + if (!start || !end) { + return false; + } + return this._areCoordsInSelection([x, y], start, end); + } + protected _areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean { return (coords[1] > start[1] && coords[1] < end[1]) || (start[1] === end[1] && coords[1] === start[1] && coords[0] >= start[0] && coords[0] < end[0]) || diff --git a/src/browser/services/Services.ts b/src/browser/services/Services.ts index 7191d0ed..c5328f76 100644 --- a/src/browser/services/Services.ts +++ b/src/browser/services/Services.ts @@ -101,6 +101,7 @@ export interface ISelectionService { shouldForceSelection(event: MouseEvent): boolean; refresh(isLinuxMouseSelection?: boolean): void; onMouseDown(event: MouseEvent): void; + isCellInSelection(x: number, y: number): boolean; } export const ISoundService = createDecorator('SoundService');