Support setting the layer of a decoration

Fixes #3778
This commit is contained in:
Daniel Imms
2022-05-12 10:51:11 -07:00
parent bc4158c9b8
commit 7b4423819e
10 changed files with 62 additions and 8 deletions
+7 -1
View File
@@ -115,6 +115,11 @@ export class SearchAddon implements ITerminalAddon {
}
}
public clearActiveDecoration(): void {
this._selectedDecoration?.dispose();
this._selectedDecoration = undefined;
}
/**
* Find the next instance of the term, then scroll to and select it. If it
* doesn't exist, do nothing.
@@ -655,7 +660,7 @@ export class SearchAddon implements ITerminalAddon {
*/
private _selectResult(result: ISearchResult | undefined, options?: ISearchDecorationOptions, noScroll?: boolean): boolean {
const terminal = this._terminal!;
this._selectedDecoration?.dispose();
this.clearActiveDecoration();
if (!result) {
terminal.clearSelection();
return false;
@@ -669,6 +674,7 @@ export class SearchAddon implements ITerminalAddon {
x: result.col,
width: result.size,
backgroundColor: options.activeMatchBackground,
layer: 'top',
overviewRulerOptions: {
color: options.activeMatchColorOverviewRuler
}
@@ -111,6 +111,13 @@ declare module 'xterm-addon-search' {
*/
public clearDecorations(): void;
/**
* Clears the active result decoration, this decoration is applied on top of the selection so
* removing it will reveal the selection underneath. This is intended to be call on the search
* textarea's `blur` event.
*/
public clearActiveDecoration(): void;
/**
* When decorations are enabled, fires when
* the search results change.
+14 -3
View File
@@ -380,17 +380,28 @@ export class WebglRenderer extends Disposable implements IRenderer {
this._workColors.bg = this._workCell.bg;
this._workColors.fg = this._workCell.fg;
// Get any foreground/background overrides, this happens on the model to avoid spreading
// override logic throughout the different sub-renderers
let bgOverride: number | undefined;
let fgOverride: number | undefined;
// Apply decorations on the bottom layer
for (const d of this._decorationService.getDecorationsAtCell(x, y, 'bottom')) {
if (d.backgroundColorRGB) {
bgOverride = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
}
if (d.foregroundColorRGB) {
fgOverride = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
}
}
// Apply the selection color if needed
if (this._isCellSelected(x, y)) {
bgOverride = this._colors.selectionOpaque.rgba >> 8 & 0xFFFFFF;
}
// Get any decoration foreground/background overrides, this happens on the model to avoid
// spreading decoration override logic throughout the different sub-renderers
for (const d of this._decorationService.getDecorationsAtCell(x, y)) {
// Apply decorations on the top layer
for (const d of this._decorationService.getDecorationsAtCell(x, y, 'top')) {
if (d.backgroundColorRGB) {
bgOverride = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
}
+6 -1
View File
@@ -212,10 +212,15 @@ function createTerminal(): void {
addDomListener(actionElements.findNext, 'keyup', (e) => {
addons.search.instance.findNext(actionElements.findNext.value, getSearchOptions(e));
});
addDomListener(actionElements.findPrevious, 'keyup', (e) => {
addons.search.instance.findPrevious(actionElements.findPrevious.value, getSearchOptions(e));
});
addDomListener(actionElements.findNext, 'blur', (e) => {
addons.search.instance.clearActiveDecoration();
});
addDomListener(actionElements.findPrevious, 'blur', (e) => {
addons.search.instance.clearActiveDecoration();
});
// fit is called within a setTimeout, cols and rows need this.
setTimeout(() => {
+5
View File
@@ -443,13 +443,18 @@ export abstract class BaseRenderLayer implements IRenderLayer {
// exist but applied after inverse
let bgOverride: number | undefined;
let fgOverride: number | undefined;
let isTop = false;
for (const d of this._decorationService.getDecorationsAtCell(x, y)) {
if (d.options.layer !== 'top' && isTop) {
continue;
}
if (d.backgroundColorRGB) {
bgOverride = d.backgroundColorRGB.rgba;
}
if (d.foregroundColorRGB) {
fgOverride = d.foregroundColorRGB.rgba;
}
isTop = d.options.layer === 'top';
}
if (!bgOverride && !fgOverride && (this._optionsService.rawOptions.minimumContrastRatio === 1 || isPowerlineGlyph(cell.getCode()))) {
+5
View File
@@ -179,10 +179,15 @@ export class TextRenderLayer extends BaseRenderLayer {
// Get any decoration foreground/background overrides, this must be fetched before the early
// exist but applied after inverse
let isTop = false;
for (const d of this._decorationService.getDecorationsAtCell(x, this._bufferService.buffer.ydisp + y)) {
if (d.options.layer !== 'top' && isTop) {
continue;
}
if (d.backgroundColorRGB) {
nextFillStyle = d.backgroundColorRGB.css;
}
isTop = d.options.layer === 'top';
}
if (prevFillStyle === null) {
@@ -177,7 +177,11 @@ export class DomRendererRowFactory {
// been applied
let bgOverride: IColor | undefined;
let fgOverride: IColor | undefined;
let isTop = false;
for (const d of this._decorationService.getDecorationsAtCell(x, row)) {
if (d.options.layer !== 'top' && isTop) {
continue;
}
if (d.backgroundColorRGB) {
bgColorMode = Attributes.CM_RGB;
bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
@@ -188,6 +192,7 @@ export class DomRendererRowFactory {
fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
fgOverride = d.foregroundColorRGB;
}
isTop = d.options.layer === 'top';
}
// Foreground
+2 -2
View File
@@ -64,13 +64,13 @@ export class DecorationService extends Disposable implements IDecorationService
return this._decorations.getKeyIterator(line);
}
public *getDecorationsAtCell(x: number, line: number): IterableIterator<IInternalDecoration> {
public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator<IInternalDecoration> {
let xmin = 0;
let xmax = 0;
for (const d of this._decorations.getKeyIterator(line)) {
xmin = d.options.x ?? 0;
xmax = xmin + (d.options.width ?? 1);
if (x >= xmin && x < xmax) {
if (x >= xmin && x < xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) {
yield d;
}
}
+1 -1
View File
@@ -312,7 +312,7 @@ export interface IDecorationService extends IDisposable {
/** Iterates over the decorations at a line (in no particular order). */
getDecorationsAtLine(line: number): IterableIterator<IInternalDecoration>;
/** Iterates over the decorations at a cell (in no particular order). */
getDecorationsAtCell(x: number, line: number): IterableIterator<IInternalDecoration>;
getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator<IInternalDecoration>;
}
export interface IInternalDecoration extends IDecoration {
readonly options: IDecorationOptions;
+10
View File
@@ -500,6 +500,16 @@ declare module 'xterm' {
*/
readonly foregroundColor?: string;
/**
* What layer to render the decoration at when {@link backgroundColor} or
* {@link foregroundColor} are used. `'bottom'` will render under the selection, `'top`' will
* render above the selection\*.
*
* *\* The selection will render on top regardless of layer on the canvas renderer due to how
* it renders selection separately.*
*/
readonly layer?: 'bottom' | 'top';
/**
* When defined, renders the decoration in the overview ruler to the right
* of the terminal. {@link ITerminalOptions.overviewRulerWidth} must be set