diff --git a/src/MouseHelper.test.ts b/src/MouseHelper.test.ts index 0925d49f..7ca165e8 100644 --- a/src/MouseHelper.test.ts +++ b/src/MouseHelper.test.ts @@ -3,62 +3,49 @@ * @license MIT */ -import jsdom = require('jsdom'); import { assert } from 'chai'; import { MouseHelper } from './MouseHelper'; -import { MockCharMeasure, MockRenderer } from './TestUtils.test'; +import { MockRenderer, MockCharSizeService } from './TestUtils.test'; const CHAR_WIDTH = 10; const CHAR_HEIGHT = 20; describe('MouseHelper.getCoords', () => { - let dom: jsdom.JSDOM; - let window: Window; - let document: Document; let mouseHelper: MouseHelper; - let charMeasure: MockCharMeasure; - beforeEach(() => { - dom = new jsdom.JSDOM(''); - window = dom.window; - document = window.document; - charMeasure = new MockCharMeasure(); - charMeasure.width = CHAR_WIDTH; - charMeasure.height = CHAR_HEIGHT; const renderer = new MockRenderer(); renderer.dimensions = { actualCellWidth: CHAR_WIDTH, actualCellHeight: CHAR_HEIGHT }; - mouseHelper = new MouseHelper(renderer as any); + mouseHelper = new MouseHelper(renderer as any, new MockCharSizeService(CHAR_WIDTH, CHAR_HEIGHT)); }); describe('when charMeasure is not initialized', () => { it('should return null', () => { - charMeasure = new MockCharMeasure(); - assert.equal(mouseHelper.getCoords({ clientX: 0, clientY: 0 }, document.createElement('div'), charMeasure, 10, 10), null); + assert.equal(mouseHelper.getCoords({ clientX: 0, clientY: 0 }, document.createElement('div'), 10, 10), null); }); }); it('should return the cell that was clicked', () => { let coords: [number, number]; - coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH / 2, clientY: CHAR_HEIGHT / 2 }, document.createElement('div'), charMeasure, 10, 10); + coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH / 2, clientY: CHAR_HEIGHT / 2 }, document.createElement('div'), 10, 10); assert.deepEqual(coords, [1, 1]); - coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH, clientY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 10, 10); + coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH, clientY: CHAR_HEIGHT }, document.createElement('div'), 10, 10); assert.deepEqual(coords, [1, 1]); - coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH, clientY: CHAR_HEIGHT + 1 }, document.createElement('div'), charMeasure, 10, 10); + coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH, clientY: CHAR_HEIGHT + 1 }, document.createElement('div'), 10, 10); assert.deepEqual(coords, [1, 2]); - coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH + 1, clientY: CHAR_HEIGHT }, document.createElement('div'), charMeasure, 10, 10); + coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH + 1, clientY: CHAR_HEIGHT }, document.createElement('div'), 10, 10); assert.deepEqual(coords, [2, 1]); }); it('should ensure the coordinates are returned within the terminal bounds', () => { let coords: [number, number]; - coords = mouseHelper.getCoords({ clientX: -1, clientY: -1 }, document.createElement('div'), charMeasure, 10, 10); + coords = mouseHelper.getCoords({ clientX: -1, clientY: -1 }, document.createElement('div'), 10, 10); assert.deepEqual(coords, [1, 1]); // Event are double the cols/rows - coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH * 20, clientY: CHAR_HEIGHT * 20 }, document.createElement('div'), charMeasure, 10, 10); + coords = mouseHelper.getCoords({ clientX: CHAR_WIDTH * 20, clientY: CHAR_HEIGHT * 20 }, document.createElement('div'), 10, 10); assert.deepEqual(coords, [10, 10], 'coordinates should never come back as larger than the terminal'); }); }); diff --git a/src/MouseHelper.ts b/src/MouseHelper.ts index 12905222..091d00ae 100644 --- a/src/MouseHelper.ts +++ b/src/MouseHelper.ts @@ -3,12 +3,14 @@ * @license MIT */ -import { ICharMeasure, IMouseHelper } from './Types'; +import { IMouseHelper } from './Types'; import { RenderCoordinator } from './renderer/RenderCoordinator'; +import { ICharSizeService } from 'ui/services/Services'; export class MouseHelper implements IMouseHelper { constructor( - private _renderCoordinator: RenderCoordinator + private _renderCoordinator: RenderCoordinator, + private _charSizeService: ICharSizeService ) { } @@ -30,9 +32,9 @@ export class MouseHelper implements IMouseHelper { * apply an offset to the x value such that the left half of the cell will * select that cell and the right half will select the next cell. */ - public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number, isSelection?: boolean): [number, number] { - // Coordinates cannot be measured if charMeasure has not been initialized - if (!charMeasure.width || !charMeasure.height) { + public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] { + // Coordinates cannot be measured if there are no valid + if (!this._charSizeService.hasValidSize) { return null; } @@ -63,8 +65,8 @@ export class MouseHelper implements IMouseHelper { * @param colCount The number of columns in the terminal. * @param rowCount The number of rows in the terminal. */ - public getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number): { x: number, y: number } { - const coords = this.getCoords(event, element, charMeasure, colCount, rowCount); + public getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } { + const coords = this.getCoords(event, element, colCount, rowCount); let x = coords[0]; let y = coords[1]; diff --git a/src/MouseZoneManager.ts b/src/MouseZoneManager.ts index 737bfc91..109bc517 100644 --- a/src/MouseZoneManager.ts +++ b/src/MouseZoneManager.ts @@ -203,7 +203,7 @@ export class MouseZoneManager extends Disposable implements IMouseZoneManager { } private _findZoneEventAt(e: MouseEvent): IMouseZone { - const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.charMeasure, this._terminal.cols, this._terminal.rows); + const coords = this._terminal.mouseHelper.getCoords(e, this._terminal.screenElement, this._terminal.cols, this._terminal.rows); if (!coords) { return null; } diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 21283b8f..d0cba71d 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -354,7 +354,7 @@ export class SelectionManager implements ISelectionManager { * @param event The mouse event. */ private _getMouseBufferCoords(event: MouseEvent): [number, number] { - const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._charMeasure, this._terminal.cols, this._terminal.rows, true); + const coords = this._terminal.mouseHelper.getCoords(event, this._terminal.screenElement, this._terminal.cols, this._terminal.rows, true); if (!coords) { return null; } diff --git a/src/Terminal.ts b/src/Terminal.ts index 6feedcbb..f68d3105 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -55,6 +55,8 @@ import { ColorManager } from 'ui/ColorManager'; import { RenderCoordinator } from './renderer/RenderCoordinator'; import { IOptionsService } from 'common/options/Types'; import { OptionsService } from 'common/options/OptionsService'; +import { ICharSizeService } from 'ui/services/Services'; +import { CharSizeService } from 'ui/services/CharSizeService'; // Let it work inside Node.js for automated testing purposes. const document = (typeof window !== 'undefined') ? window.document : null; @@ -107,9 +109,12 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II private _customKeyEventHandler: CustomKeyEventHandler; - // services + // common services public optionsService: IOptionsService; + // browser services + private _charSizeService: ICharSizeService; + // modes public applicationKeypad: boolean; public applicationCursor: boolean; @@ -615,6 +620,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this._helperContainer.appendChild(this._compositionView); this.charMeasure = new CharMeasure(document, this._helperContainer); + this._charSizeService = new CharSizeService(this._document, this._helperContainer, this.optionsService); // Performance: Add viewport and helper elements from the fragment this.element.appendChild(fragment); @@ -658,7 +664,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II })); this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this.selectionManager.refresh())); - this.mouseHelper = new MouseHelper(this._renderCoordinator); + this.mouseHelper = new MouseHelper(this._renderCoordinator, this._charSizeService); // apply mouse event classes set by escape codes before terminal was attached this.element.classList.toggle('enable-mouse-events', this.mouseEvents); if (this.mouseEvents) { @@ -738,7 +744,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II button = getButton(ev); // get mouse coordinates - pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.cols, self.rows); + pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.cols, self.rows); if (!pos) return; sendEvent(button, pos); @@ -764,7 +770,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II // ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7< function sendMove(ev: MouseEvent): void { let button = pressed; - const pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.charMeasure, self.cols, self.rows); + const pos = self.mouseHelper.getRawByteCoords(ev, self.screenElement, self.cols, self.rows); if (!pos) return; // buttons marked as motions diff --git a/src/TestUtils.test.ts b/src/TestUtils.test.ts index e44489ea..32b7a65c 100644 --- a/src/TestUtils.test.ts +++ b/src/TestUtils.test.ts @@ -14,6 +14,7 @@ import { Terminal } from './Terminal'; import { AttributeData } from 'core/buffer/BufferLine'; import { IColorManager, IColorSet } from 'ui/Types'; import { IOptionsService } from 'common/options/Types'; +import { ICharSizeService } from 'ui/services/Services'; export class TestTerminal extends Terminal { writeSync(data: string): void { @@ -182,15 +183,6 @@ export class MockTerminal implements ITerminal { deregisterCharacterJoiner(joinerId: number): void { } } -export class MockCharMeasure implements ICharMeasure { - onCharSizeChanged: IEvent; - width: number; - height: number; - measure(options: ITerminalOptions): void { - throw new Error('Method not implemented.'); - } -} - export class MockInputHandlingTerminal implements IInputHandlingTerminal { element: HTMLElement; options: ITerminalOptions = {}; @@ -438,3 +430,10 @@ export class MockCompositionHelper implements ICompositionHelper { return true; } } + +export class MockCharSizeService implements ICharSizeService { + get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } + onCharSizeChange: IEvent; + constructor(public width: number, public height: number) {} + measure(): void {} +} diff --git a/src/Types.ts b/src/Types.ts index 7b9c68c5..0aecdc96 100644 --- a/src/Types.ts +++ b/src/Types.ts @@ -284,8 +284,8 @@ export interface ILinkifierAccessor { } export interface IMouseHelper { - getCoords(event: { clientX: number, clientY: number }, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number, isSelection?: boolean): [number, number]; - getRawByteCoords(event: MouseEvent, element: HTMLElement, charMeasure: ICharMeasure, colCount: number, rowCount: number): { x: number, y: number }; + getCoords(event: { clientX: number, clientY: number }, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number]; + getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number }; } export interface ICharMeasure { diff --git a/src/handlers/AltClickHandler.ts b/src/handlers/AltClickHandler.ts index 43c26498..eebabd7d 100644 --- a/src/handlers/AltClickHandler.ts +++ b/src/handlers/AltClickHandler.ts @@ -33,7 +33,6 @@ export class AltClickHandler { const coordinates = this._terminal.mouseHelper.getCoords( this._mouseEvent, this._terminal.element, - this._terminal.charMeasure, this._terminal.cols, this._terminal.rows, false diff --git a/src/ui/services/CharSizeService.ts b/src/ui/services/CharSizeService.ts index 2fddb802..7d9d3a2c 100644 --- a/src/ui/services/CharSizeService.ts +++ b/src/ui/services/CharSizeService.ts @@ -12,7 +12,7 @@ export class CharSizeService implements ICharSizeService { public height: number = 0; private _measureStrategy: IMeasureStrategy; - public get hasValidDimensions(): boolean { return this.width > 0 && this.height > 0; } + public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; } private _onCharSizeChange = new EventEmitter2(); public get onCharSizeChange(): IEvent { return this._onCharSizeChange.event; } diff --git a/src/ui/services/Services.d.ts b/src/ui/services/Services.d.ts index 51dd043a..17cc4f3b 100644 --- a/src/ui/services/Services.d.ts +++ b/src/ui/services/Services.d.ts @@ -8,7 +8,7 @@ import { IEvent } from 'common/EventEmitter2'; export interface ICharSizeService { readonly width: number; readonly height: number; - readonly hasValidDimensions: boolean; + readonly hasValidSize: boolean; readonly onCharSizeChange: IEvent;