]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Get selection partially rendering
authorDaniel Imms <daimms@microsoft.com>
Sat, 20 May 2017 06:29:51 +0000 (23:29 -0700)
committerDaniel Imms <daimms@microsoft.com>
Sat, 20 May 2017 06:29:51 +0000 (23:29 -0700)
src/Interfaces.ts
src/Renderer.ts
src/SelectionManager.ts
src/xterm.css
src/xterm.js

index ca228ce01bdfb99e8218a887c71a1de3b61be016..2f2bef7315c20c33ed606cee3e7be008f7520b6d 100644 (file)
@@ -20,6 +20,8 @@ export interface IBrowser {
 export interface ITerminal {
   element: HTMLElement;
   rowContainer: HTMLElement;
+  selectionContainer: HTMLElement;
+  charMeasure: ICharMeasure;
   textarea: HTMLTextAreaElement;
   ybase: number;
   ydisp: number;
index 21d483283250e16be451aab0b5e97110046bab49..b78567d5896ac6909ab44d446efa11a4f5a27b02 100644 (file)
@@ -321,6 +321,68 @@ export class Renderer {
 
   public refreshSelection(start: [number, number], end: [number, number]) {
     console.log('renderer, refresh:', start, end);
+
+    // Remove all selections
+    while (this._terminal.selectionContainer.children.length) {
+      this._terminal.selectionContainer.removeChild(this._terminal.selectionContainer.children[0]);
+    }
+
+    // Selection does not exist
+    if (!start || !end) {
+      return;
+    }
+
+    // Swap the start and end if necessary
+    if (start[1] > end[1] || (start[1] === end[1] && start[0] > end[0])) {
+      const temp = start;
+      start = end;
+      end = temp;
+    }
+
+    // Translate from buffer position to viewport position
+    const viewportStartRow = start[1] - this._terminal.ydisp;
+    const viewportEndRow = end[1] - this._terminal.ydisp;
+    const viewportCappedStartRow = Math.max(viewportStartRow, 0);
+    const viewportCappedEndRow = Math.min(viewportEndRow, this._terminal.rows - 1);
+
+    // No need to draw the selection
+    if (viewportCappedStartRow >= this._terminal.rows || viewportCappedEndRow < 0) {
+      return;
+    }
+
+    console.log('viewportStartRow', viewportCappedStartRow);
+    console.log('viewportEndRow', viewportCappedEndRow);
+
+    // TODO: Only redraw selections when necessary
+
+    // Create the selections
+    const documentFragment = document.createDocumentFragment();
+    // Draw first row
+    const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0;
+    const endCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols;
+    documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol));
+    // Draw middle rows
+    for (let i = viewportCappedStartRow + 1; i < viewportCappedEndRow; i++) {
+      documentFragment.appendChild(this._createSelectionElement(i, 0, this._terminal.cols));
+    }
+    // Draw final row
+    if (viewportCappedStartRow !== viewportCappedEndRow) {
+      // Only draw viewportEndRow if it's not the same as viewporttartRow
+      const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._terminal.cols;
+      documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol));
+    }
+    this._terminal.selectionContainer.appendChild(documentFragment);
+  }
+
+  private _createSelectionElement(row: number, colStart: number, colEnd: number): HTMLElement {
+    const element = document.createElement('div');
+    // TODO: Move into a generated <style> element
+    element.style.height = `${this._terminal.charMeasure.height}px`;
+
+    element.style.top = `${row * this._terminal.charMeasure.height}px`;
+    element.style.left = `${colStart * this._terminal.charMeasure.width}px`;
+    element.style.width = `${this._terminal.charMeasure.width * (colEnd - colStart)}px`;
+    return element;
   }
 }
 
index e0e5bdbc3cbdfd80f8d2a3d90860c51a97eee6fd..6b8a1dd6ea1993d9e35873d9707d666fb4bd70a5 100644 (file)
@@ -6,25 +6,23 @@ import { CharMeasure } from './utils/CharMeasure';
 import { CircularList } from './utils/CircularList';
 import { EventEmitter } from './EventEmitter';
 import * as Mouse from './utils/Mouse';
+import { ITerminal } from './Interfaces';
 
 export class SelectionManager extends EventEmitter {
   // TODO: Create a SelectionModel
   private _selectionStart: [number, number];
   private _selectionEnd: [number, number];
 
-  private _buffer: CircularList<any>;
-  private _rowContainer: HTMLElement;
-  private _selectionContainer: HTMLElement;
-  private _charMeasure: CharMeasure;
-
   private _mouseMoveListener: EventListener;
 
-  constructor(buffer: CircularList<any>, rowContainer: HTMLElement, selectionContainer: HTMLElement, charMeasure: CharMeasure) {
+  constructor(
+    private _terminal: ITerminal,
+    private _buffer: CircularList<any>,
+    private _rowContainer: HTMLElement,
+    private _selectionContainer: HTMLElement,
+    private _charMeasure: CharMeasure
+  ) {
     super();
-    this._rowContainer = rowContainer;
-    this._selectionContainer = selectionContainer;
-    this._buffer = buffer;
-    this._charMeasure = charMeasure;
     this._attachListeners();
   }
 
@@ -49,8 +47,6 @@ export class SelectionManager extends EventEmitter {
   public refresh(): void {
     // TODO: Figure out when to refresh the selection vs when to refresh the viewport
     this.emit('refresh', { start: this._selectionStart, end: this._selectionEnd });
-    console.log(`Selection: Start: (${this._selectionStart[0]}, ${this._selectionStart[1]}), End: (${this._selectionEnd[0]}, ${this._selectionEnd[1]})`);
-    this._selectionContainer.innerHTML = `<div><br><br></div>`;
   }
 
   /**
@@ -85,14 +81,21 @@ export class SelectionManager extends EventEmitter {
   }
 
   private _getMouseBufferCoords(event: MouseEvent) {
-    // TODO: Take into account the current terminal viewport when fetching coordinates
-    return Mouse.getCoords(event, this._rowContainer, this._charMeasure);
+    const coords = Mouse.getCoords(event, this._rowContainer, this._charMeasure);
+    // Convert to 0-based
+    coords[0]--;
+    coords[1]--;
+    // Convert viewport coords to buffer coords
+    coords[1] += this._terminal.ydisp;
+    return coords;
   }
 
   private _onMouseDown(event: MouseEvent) {
     this._selectionStart = this._getMouseBufferCoords(event);
     if (this._selectionStart) {
+      this._selectionEnd = null;
       this._rowContainer.addEventListener('mousemove', this._mouseMoveListener);
+      this.refresh();
     }
   }
 
index 54f5331642bb09325486b5a47a5f06debaa59b33..381dd53c4806d2b242b92921e881698fce560501 100644 (file)
 }
 
 .terminal .xterm-selection div {
+    position: absolute;
     background-color: #777;
 }
 
index 028debb692304882875cc1f4216256bbaec12710..e75d90e90a626ca3e185ae98e5893f03c06c68f9 100644 (file)
@@ -697,8 +697,12 @@ Terminal.prototype.open = function(parent, focus) {
 
   this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure);
   this.renderer = new Renderer(this);
-  this.selectionManager = new SelectionManager(this.lines, this.rowContainer, this.selectionContainer, this.charMeasure);
+  this.selectionManager = new SelectionManager(this, this.lines, this.rowContainer, this.selectionContainer, this.charMeasure);
   this.selectionManager.on('refresh', data => this.renderer.refreshSelection(data.start, data.end));
+  this.on('scroll', () => {
+    console.log('scroll');
+    this.selectionManager.refresh();
+  });
 
   // Setup loop that draws to screen
   this.refresh(0, this.rows - 1);