2 * Copyright (c) 2017 The xterm.js authors. All rights reserved.
6 import { IRenderDimensions } from 'browser/renderer/Types';
7 import { BaseRenderLayer } from 'browser/renderer/BaseRenderLayer';
8 import { IColorSet } from 'browser/Types';
9 import { IBufferService, IOptionsService } from 'common/services/Services';
11 interface ISelectionState {
12 start?: [number, number];
13 end?: [number, number];
14 columnSelectMode?: boolean;
18 export class SelectionRenderLayer extends BaseRenderLayer {
19 private _state!: ISelectionState;
22 container: HTMLElement,
26 readonly bufferService: IBufferService,
27 readonly optionsService: IOptionsService
29 super(container, 'selection', zIndex, true, colors, rendererId, bufferService, optionsService);
33 private _clearState(): void {
37 columnSelectMode: undefined,
42 public resize(dim: IRenderDimensions): void {
44 // Resizing the canvas discards the contents of the canvas so clear state
48 public reset(): void {
49 if (this._state.start && this._state.end) {
55 public onSelectionChanged(start: [number, number], end: [number, number], columnSelectMode: boolean): void {
56 // Selection has not changed
57 if (!this._didStateChange(start, end, columnSelectMode, this._bufferService.buffer.ydisp)) {
61 // Remove all selections
64 // Selection does not exist
70 // Translate from buffer position to viewport position
71 const viewportStartRow = start[1] - this._bufferService.buffer.ydisp;
72 const viewportEndRow = end[1] - this._bufferService.buffer.ydisp;
73 const viewportCappedStartRow = Math.max(viewportStartRow, 0);
74 const viewportCappedEndRow = Math.min(viewportEndRow, this._bufferService.rows - 1);
76 // No need to draw the selection
77 if (viewportCappedStartRow >= this._bufferService.rows || viewportCappedEndRow < 0) {
81 this._ctx.fillStyle = this._colors.selection.css;
83 if (columnSelectMode) {
84 const startCol = start[0];
85 const width = end[0] - startCol;
86 const height = viewportCappedEndRow - viewportCappedStartRow + 1;
87 this._fillCells(startCol, viewportCappedStartRow, width, height);
90 const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
91 const startRowEndCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
92 this._fillCells(startCol, viewportCappedStartRow, startRowEndCol - startCol, 1);
95 const middleRowsCount = Math.max(viewportCappedEndRow - viewportCappedStartRow - 1, 0);
96 this._fillCells(0, viewportCappedStartRow + 1, this._bufferService.cols, middleRowsCount);
99 if (viewportCappedStartRow !== viewportCappedEndRow) {
100 // Only draw viewportEndRow if it's not the same as viewportStartRow
101 const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._bufferService.cols;
102 this._fillCells(0, viewportCappedEndRow, endCol, 1);
106 // Save state for next render
107 this._state.start = [start[0], start[1]];
108 this._state.end = [end[0], end[1]];
109 this._state.columnSelectMode = columnSelectMode;
110 this._state.ydisp = this._bufferService.buffer.ydisp;
113 private _didStateChange(start: [number, number], end: [number, number], columnSelectMode: boolean, ydisp: number): boolean {
114 return !this._areCoordinatesEqual(start, this._state.start) ||
115 !this._areCoordinatesEqual(end, this._state.end) ||
116 columnSelectMode !== this._state.columnSelectMode ||
117 ydisp !== this._state.ydisp;
120 private _areCoordinatesEqual(coord1: [number, number] | undefined, coord2: [number, number] | undefined): boolean {
121 if (!coord1 || !coord2) {
125 return coord1[0] === coord2[0] && coord1[1] === coord2[1];