Create color zone system

Part of #3703
This commit is contained in:
Daniel Imms
2022-04-01 05:30:01 -07:00
parent 23afd53301
commit 7c5853bf00
2 changed files with 118 additions and 1 deletions
+94
View File
@@ -0,0 +1,94 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInternalDecoration } from 'common/services/Services';
import { IDecorationOverviewRulerOptions } from 'xterm';
export interface IColorZoneStore {
readonly zones: IColorZone[];
clear(): void;
addDecoration(decoration: IInternalDecoration): void;
/**
* Sets the amount of padding in lines that will be added between zones, if new lines intersect
* the padding they will be merged into the same zone.
*/
setPadding(padding: { [position: string]: number }): void;
}
export interface IColorZone {
/** Color in a format supported by canvas' fillStyle. */
color: string;
position: 'full' | 'left' | 'center' | 'right' | undefined;
startBufferLine: number;
endBufferLine: number;
}
export class ColorZoneStore implements IColorZoneStore {
private _zones: IColorZone[] = [];
public get zones(): IColorZone[] { return this._zones; }
private _linePadding: { [position: string]: number } = {
full: 0,
left: 0,
center: 0,
right: 0
};
public clear(): void {
this._zones.length = 0;
}
public addDecoration(decoration: IInternalDecoration): void {
if (!decoration.options.overviewRulerOptions) {
return;
}
for (const z of this._zones) {
if (z.color === decoration.options.overviewRulerOptions.color &&
z.position === decoration.options.overviewRulerOptions.position) {
if (this._lineIntersectsZone(z, decoration.marker.line)) {
console.log('intersects, skip');
// this._addLineToZone(z, decoration.marker.line);
return;
}
if (this._lineAdjacentToZone(z, decoration.marker.line, decoration.options.overviewRulerOptions.position)) {
this._addLineToZone(z, decoration.marker.line);
console.log('adjacent, add');
return;
}
}
}
// TODO: Track zones in an object pool to reduce GC
this._zones.push({
color: decoration.options.overviewRulerOptions.color,
position: decoration.options.overviewRulerOptions.position,
startBufferLine: decoration.marker.line,
endBufferLine: decoration.marker.line
});
}
public setPadding(padding: { [position: string]: number }): void {
console.log('padding', padding);
this._linePadding = padding;
}
private _lineIntersectsZone(zone: IColorZone, line: number): boolean {
return (
line >= zone.startBufferLine &&
line <= zone.endBufferLine
);
}
private _lineAdjacentToZone(zone: IColorZone, line: number, position: IColorZone['position']): boolean {
return (
(line >= zone.startBufferLine - 1 - this._linePadding[position || 'full'] * 2) &&
(line <= zone.endBufferLine + 1 + this._linePadding[position || 'full'] * 2)
);
}
private _addLineToZone(zone: IColorZone, line: number): void {
zone.startBufferLine = Math.min(zone.startBufferLine, line);
zone.endBufferLine = Math.max(zone.endBufferLine, line);
}
}
@@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ColorZoneStore, IColorZoneStore } from 'browser/Decorations/ColorZoneStore';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IRenderService } from 'browser/services/Services';
import { Disposable } from 'common/Lifecycle';
@@ -33,6 +34,7 @@ export class OverviewRulerRenderer extends Disposable {
private readonly _canvas: HTMLCanvasElement;
private readonly _ctx: CanvasRenderingContext2D;
private readonly _decorationElements: Map<IInternalDecoration, HTMLElement> = new Map();
private readonly _colorZoneStore: IColorZoneStore = new ColorZoneStore();
private get _width(): number {
return this._optionsService.options.overviewRulerWidth || 0;
}
@@ -40,6 +42,7 @@ export class OverviewRulerRenderer extends Disposable {
private _shouldUpdateDimensions: boolean | undefined = true;
private _shouldUpdateAnchor: boolean | undefined = true;
private _lastKnownBufferLength: number = 0;
private _containerHeight: number | undefined;
@@ -84,6 +87,11 @@ export class OverviewRulerRenderer extends Disposable {
this.register(this._bufferService.buffers.onBufferActivate(() => {
this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
}));
this.register(this._bufferService.onScroll(() => {
if (this._lastKnownBufferLength !== this._bufferService.buffers.normal.lines.length) {
this._refreshColorZonePadding();
}
}));
}
/**
* On dimension change, update canvas dimensions
@@ -140,6 +148,17 @@ export class OverviewRulerRenderer extends Disposable {
drawX.right = drawWidth.left + drawWidth.center;
}
private _refreshColorZonePadding(): void {
const nonFullPadding = Math.ceil(this._bufferService.buffers.active.lines.length / (this._canvas.height - 1) * (drawHeight.full / 2));
this._colorZoneStore.setPadding({
full: Math.ceil(this._bufferService.buffers.active.lines.length / (this._canvas.height - 1) * (drawHeight.full / 2)),
left: nonFullPadding,
center: nonFullPadding,
right: nonFullPadding
});
this._lastKnownBufferLength = this._bufferService.buffers.normal.lines.length;
}
private _refreshStyle(decoration: IInternalDecoration): void {
if (!decoration.options.overviewRulerOptions) {
this._decorationElements.delete(decoration);
@@ -151,7 +170,7 @@ export class OverviewRulerRenderer extends Disposable {
/* x */ drawX[decoration.options.overviewRulerOptions.position!],
/* y */ Math.round(
(this._canvas.height - 1) * // -1 to ensure at least 2px are allowed for decoration on last line
(decoration.options.marker.line / this._bufferService.buffers.active.lines.length) - drawHeight[decoration.options.overviewRulerOptions.position!] / 2
(decoration.options.marker.line / this._bufferService.buffers.active.lines.length) - drawHeight[decoration.options.overviewRulerOptions.position!] / 2
),
/* w */ drawWidth[decoration.options.overviewRulerOptions.position!],
/* h */ drawHeight[decoration.options.overviewRulerOptions.position!]
@@ -164,6 +183,7 @@ export class OverviewRulerRenderer extends Disposable {
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
this._canvas.height = Math.round(this._screenElement.clientHeight * window.devicePixelRatio);
this._refreshDrawConstants();
this._refreshColorZonePadding();
}
private _refreshDecorations(): void {
@@ -171,7 +191,9 @@ export class OverviewRulerRenderer extends Disposable {
this._refreshCanvasDimensions();
}
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
this._colorZoneStore.clear();
for (const decoration of this._decorationService.decorations) {
this._colorZoneStore.addDecoration(decoration);
if (decoration.options.overviewRulerOptions && decoration.options.overviewRulerOptions.position !== 'full') {
this._renderDecoration(decoration);
}
@@ -181,6 +203,7 @@ export class OverviewRulerRenderer extends Disposable {
this._renderDecoration(decoration);
}
}
console.log('zones', this._colorZoneStore.zones);
this._shouldUpdateDimensions = false;
this._shouldUpdateAnchor = false;
}