mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Adopt CharSizeService in MouseHelper
This commit is contained in:
+9
-22
@@ -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 = <any>{
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
+9
-7
@@ -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];
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+10
-4
@@ -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
|
||||
|
||||
@@ -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<void>;
|
||||
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<string>;
|
||||
constructor(public width: number, public height: number) {}
|
||||
measure(): void {}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<string>();
|
||||
public get onCharSizeChange(): IEvent<string> { return this._onCharSizeChange.event; }
|
||||
|
||||
Vendored
+1
-1
@@ -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<string>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user