diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts
index fdeef91c..a966e6f9 100644
--- a/src/browser/renderer/dom/DomRenderer.ts
+++ b/src/browser/renderer/dom/DomRenderer.ts
@@ -226,7 +226,7 @@ export class DomRenderer extends Disposable implements IRenderer {
`}` +
`${this._terminalSelector} .${SELECTION_CLASS} div {` +
` position: absolute;` +
- ` background-color: ${this._colors.selectionTransparent.css};` +
+ ` background-color: ${this._colors.selectionOpaque.css};` +
`}`;
// Colors
this._colors.ansi.forEach((c, i) => {
diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts
index 5a374b14..7c48cce8 100644
--- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts
+++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts
@@ -12,7 +12,7 @@ import { IBufferLine } from 'common/Types';
import { CellData } from 'common/buffer/CellData';
import { MockCoreService, MockDecorationService, MockOptionsService } from 'common/TestUtils.test';
import { css } from 'common/Color';
-import { MockCharacterJoinerService, MockSelectionService } from 'browser/TestUtils.test';
+import { MockCharacterJoinerService } from 'browser/TestUtils.test';
describe('DomRendererRowFactory', () => {
let dom: jsdom.JSDOM;
@@ -245,6 +245,26 @@ describe('DomRendererRowFactory', () => {
);
});
});
+
+ describe('selectionForeground', () => {
+ it('should force selected cells with content to be rendered above the background', () => {
+ lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
+ lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)]));
+ rowFactory.onSelectionChanged([1, 0], [2, 0], false);
+ const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
+ assert.equal(getFragmentHtml(fragment),
+ 'ab'
+ );
+ });
+ it('should force whitespace cells to be rendered above the background', () => {
+ lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)]));
+ rowFactory.onSelectionChanged([0, 0], [2, 0], false);
+ const fragment = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, 20);
+ assert.equal(getFragmentHtml(fragment),
+ ' a'
+ );
+ });
+ });
});
function getFragmentHtml(fragment: DocumentFragment): string {
diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts
index 266d7587..39fd4fdd 100644
--- a/src/browser/renderer/dom/DomRendererRowFactory.ts
+++ b/src/browser/renderer/dom/DomRendererRowFactory.ts
@@ -206,14 +206,22 @@ export class DomRendererRowFactory {
}
// Apply selection foreground if applicable
+ const isInSelection = this._isCellInSelection(x, row);
if (!isTop) {
- if (this._colors.selectionForeground && this._isCellInSelection(x, row)) {
+ if (this._colors.selectionForeground && isInSelection) {
fgColorMode = Attributes.CM_RGB;
fg = this._colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
fgOverride = this._colors.selectionForeground;
}
}
+ // If in the selection, force the element to be above the selection to improve contrast and
+ // support opaque selections
+ if (isInSelection) {
+ bgOverride = this._colors.selectionOpaque;
+ isTop = true;
+ }
+
// If it's a top decoration, render above the selection
if (isTop) {
charElement.classList.add(`xterm-decoration-top`);
@@ -226,7 +234,7 @@ export class DomRendererRowFactory {
if (cell.isBold() && fg < 8 && this._optionsService.rawOptions.drawBoldTextInBrightColors) {
fg += 8;
}
- if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.ansi[fg], cell, undefined, undefined)) {
+ if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.ansi[fg], cell, bgOverride, undefined)) {
charElement.classList.add(`xterm-fg-${fg}`);
}
break;
@@ -242,7 +250,7 @@ export class DomRendererRowFactory {
break;
case Attributes.CM_DEFAULT:
default:
- if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.foreground, cell, undefined, undefined)) {
+ if (!this._applyMinimumContrast(charElement, this._colors.background, this._colors.foreground, cell, bgOverride, undefined)) {
if (isInverse) {
charElement.classList.add(`xterm-fg-${INVERTED_DEFAULT_COLOR}`);
}
@@ -279,16 +287,14 @@ export class DomRendererRowFactory {
// Try get from cache first, only use the cache when there are no decoration overrides
let adjustedColor: IColor | undefined | null = undefined;
- if (!bgOverride || !fgOverride) {
- adjustedColor = this._colors.contrastCache.getColor(this._workCell.bg, this._workCell.fg);
+ if (!bgOverride && !fgOverride) {
+ adjustedColor = this._colors.contrastCache.getColor(bg.rgba, fg.rgba);
}
// Calculate and store in cache
if (adjustedColor === undefined) {
adjustedColor = color.ensureContrastRatio(bgOverride || bg, fgOverride || fg, this._optionsService.rawOptions.minimumContrastRatio);
- if (!bgOverride || !fgOverride) {
- this._colors.contrastCache.setColor(this._workCell.bg, this._workCell.fg, adjustedColor ?? null);
- }
+ this._colors.contrastCache.setColor((bgOverride || bg).rgba, (fgOverride || fg).rgba, adjustedColor ?? null);
}
if (adjustedColor) {