mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into 553_find_api
This commit is contained in:
@@ -209,4 +209,20 @@ describe('SelectionManager', () => {
|
||||
assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasSelection', () => {
|
||||
it('should return whether there is a selection', () => {
|
||||
selectionManager.model.selectionStart = [0, 0];
|
||||
selectionManager.model.selectionStartLength = 0;
|
||||
assert.equal(selectionManager.hasSelection, false);
|
||||
selectionManager.model.selectionEnd = [0, 0];
|
||||
assert.equal(selectionManager.hasSelection, false);
|
||||
selectionManager.model.selectionEnd = [1, 0];
|
||||
assert.equal(selectionManager.hasSelection, true);
|
||||
selectionManager.model.selectionEnd = [0, 1];
|
||||
assert.equal(selectionManager.hasSelection, true);
|
||||
selectionManager.model.selectionEnd = [1, 1];
|
||||
assert.equal(selectionManager.hasSelection, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+23
-5
@@ -2,10 +2,11 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import * as Mouse from './utils/Mouse';
|
||||
import * as Browser from './utils/Browser';
|
||||
import { CharMeasure } from './utils/CharMeasure';
|
||||
import { CircularList } from './utils/CircularList';
|
||||
import { EventEmitter } from './EventEmitter';
|
||||
import * as Mouse from './utils/Mouse';
|
||||
import { ITerminal } from './Interfaces';
|
||||
import { SelectionModel } from './SelectionModel';
|
||||
import { translateBufferLineToString } from './utils/BufferLine';
|
||||
@@ -187,7 +188,12 @@ export class SelectionManager extends EventEmitter {
|
||||
* Gets whether there is an active text selection.
|
||||
*/
|
||||
public get hasSelection(): boolean {
|
||||
return !!this._model.finalSelectionStart && !!this._model.finalSelectionEnd;
|
||||
const start = this._model.finalSelectionStart;
|
||||
const end = this._model.finalSelectionEnd;
|
||||
if (!start || !end) {
|
||||
return false;
|
||||
}
|
||||
return start[0] !== end[0] || start[1] !== end[1];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,11 +253,23 @@ export class SelectionManager extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Queues a refresh, redrawing the selection on the next opportunity.
|
||||
* @param isNewSelection Whether the selection should be registered as a new
|
||||
* selection on Linux.
|
||||
*/
|
||||
public refresh(): void {
|
||||
public refresh(isNewSelection?: boolean): void {
|
||||
// Queue the refresh for the renderer
|
||||
if (!this._refreshAnimationFrame) {
|
||||
this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh());
|
||||
}
|
||||
|
||||
// If the platform is Linux and the refresh call comes from a mouse event,
|
||||
// we need to update the selection for middle click to paste selection.
|
||||
if (Browser.isLinux && isNewSelection) {
|
||||
const selectionText = this.selectionText;
|
||||
if (selectionText.length) {
|
||||
this.emit('newselection', this.selectionText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,7 +365,7 @@ export class SelectionManager extends EventEmitter {
|
||||
}
|
||||
|
||||
this._addMouseDownListeners();
|
||||
this.refresh();
|
||||
this.refresh(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -502,7 +520,7 @@ export class SelectionManager extends EventEmitter {
|
||||
if (!previousSelectionEnd ||
|
||||
previousSelectionEnd[0] !== this._model.selectionEnd[0] ||
|
||||
previousSelectionEnd[1] !== this._model.selectionEnd[1]) {
|
||||
this.refresh();
|
||||
this.refresh(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,12 +75,11 @@ export function pasteHandler(ev: ClipboardEvent, term: ITerminal) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to right-click event and allow right-click copy and paste.
|
||||
* @param ev The original right click event to be handled
|
||||
* @param term The terminal on which to apply the handled paste event
|
||||
* @param selectionManager The terminal's selection manager.
|
||||
* Moves the textarea under the mouse cursor and focuses it.
|
||||
* @param ev The original right click event to be handled.
|
||||
* @param textarea The terminal's textarea.
|
||||
*/
|
||||
export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager) {
|
||||
export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextAreaElement) {
|
||||
// Bring textarea at the cursor position
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.style.width = '20px';
|
||||
@@ -89,10 +88,7 @@ export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement,
|
||||
textarea.style.top = (ev.clientY - 10) + 'px';
|
||||
textarea.style.zIndex = '1000';
|
||||
|
||||
// Get textarea ready to copy from the context menu
|
||||
textarea.value = selectionManager.selectionText;
|
||||
textarea.focus();
|
||||
textarea.select();
|
||||
|
||||
// Reset the terminal textarea's styling
|
||||
setTimeout(function () {
|
||||
@@ -104,3 +100,17 @@ export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement,
|
||||
textarea.style.zIndex = null;
|
||||
}, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to right-click event and allow right-click copy and paste.
|
||||
* @param ev The original right click event to be handled.
|
||||
* @param textarea The terminal's textarea.
|
||||
* @param selectionManager The terminal's selection manager.
|
||||
*/
|
||||
export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager) {
|
||||
moveTextAreaUnderMouseCursor(ev, textarea);
|
||||
|
||||
// Get textarea ready to copy from the context menu
|
||||
textarea.value = selectionManager.selectionText;
|
||||
textarea.select();
|
||||
}
|
||||
|
||||
+20
-5
@@ -371,12 +371,17 @@ describe('xterm.js', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('attachCustomEventHandler', function () {
|
||||
describe('attachCustomKeyEventHandler', function () {
|
||||
var evKeyDown = {
|
||||
preventDefault: function() {},
|
||||
stopPropagation: function() {},
|
||||
type: 'keydown'
|
||||
}
|
||||
var evKeyPress = {
|
||||
preventDefault: function() {},
|
||||
stopPropagation: function() {},
|
||||
type: 'keypress'
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
xterm.handler = function() {};
|
||||
@@ -387,29 +392,39 @@ describe('xterm.js', function() {
|
||||
bind: function() {
|
||||
return function () { return true; }
|
||||
}
|
||||
},
|
||||
keypress: {
|
||||
bind: function() {
|
||||
return function () { return true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should process the keydown event based on what the handler returns', function () {
|
||||
it('should process the keydown/keypress event based on what the handler returns', function () {
|
||||
assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), true);
|
||||
xterm.attachCustomKeydownHandler(function (ev) {
|
||||
assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), true);
|
||||
xterm.attachCustomKeyEventHandler(function (ev) {
|
||||
return ev.keyCode === 77;
|
||||
});
|
||||
assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), true);
|
||||
xterm.attachCustomKeydownHandler(function (ev) {
|
||||
assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), true);
|
||||
xterm.attachCustomKeyEventHandler(function (ev) {
|
||||
return ev.keyCode !== 77;
|
||||
});
|
||||
assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false);
|
||||
assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false);
|
||||
});
|
||||
|
||||
it('should alive after reset(ESC c Full Reset (RIS))', function () {
|
||||
xterm.attachCustomKeydownHandler(function (ev) {
|
||||
xterm.attachCustomKeyEventHandler(function (ev) {
|
||||
return ev.keyCode !== 77;
|
||||
});
|
||||
assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false);
|
||||
assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false);
|
||||
xterm.reset();
|
||||
assert.equal(xterm.keyDown(Object.assign({}, evKeyDown, { keyCode: 77 })), false);
|
||||
assert.equal(xterm.keyPress(Object.assign({}, evKeyPress, { keyCode: 77 })), false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -20,3 +20,4 @@ export const isMac = contains(['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], pla
|
||||
export const isIpad = platform === 'iPad';
|
||||
export const isIphone = platform === 'iPhone';
|
||||
export const isMSWindows = contains(['Windows', 'Win16', 'Win32', 'WinCE'], platform);
|
||||
export const isLinux = platform.indexOf('Linux') >= 0;
|
||||
|
||||
+4
-1
@@ -192,11 +192,14 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.terminal .xterm-selection div {
|
||||
position: absolute;
|
||||
background-color: #555;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+54
-14
@@ -13,7 +13,7 @@
|
||||
import { CompositionHelper } from './CompositionHelper';
|
||||
import { EventEmitter } from './EventEmitter';
|
||||
import { Viewport } from './Viewport';
|
||||
import { rightClickHandler, pasteHandler, copyHandler } from './handlers/Clipboard';
|
||||
import { rightClickHandler, moveTextAreaUnderMouseCursor, pasteHandler, copyHandler } from './handlers/Clipboard';
|
||||
import { CircularList } from './utils/CircularList';
|
||||
import { C0 } from './EscapeSequences';
|
||||
import { InputHandler } from './InputHandler';
|
||||
@@ -168,7 +168,7 @@ function Terminal(options) {
|
||||
this.queue = '';
|
||||
this.scrollTop = 0;
|
||||
this.scrollBottom = this.rows - 1;
|
||||
this.customKeydownHandler = null;
|
||||
this.customKeyEventHandler = null;
|
||||
this.cursorBlinkInterval = null;
|
||||
|
||||
// modes
|
||||
@@ -538,9 +538,11 @@ Terminal.prototype.initGlobal = function() {
|
||||
on(this.textarea, 'paste', pasteHandlerWrapper);
|
||||
on(this.element, 'paste', pasteHandlerWrapper);
|
||||
|
||||
// Handle right click context menus
|
||||
if (term.browser.isFirefox) {
|
||||
// Firefox doesn't appear to fire the contextmenu event on right click
|
||||
on(this.element, 'mousedown', event => {
|
||||
if (ev.button == 2) {
|
||||
if (event.button == 2) {
|
||||
rightClickHandler(event, this.textarea, this.selectionManager);
|
||||
}
|
||||
});
|
||||
@@ -549,6 +551,19 @@ Terminal.prototype.initGlobal = function() {
|
||||
rightClickHandler(event, this.textarea, this.selectionManager);
|
||||
});
|
||||
}
|
||||
|
||||
// Move the textarea under the cursor when middle clicking on Linux to ensure
|
||||
// middle click to paste selection works. This only appears to work in Chrome
|
||||
// at the time is writing.
|
||||
if (term.browser.isLinux) {
|
||||
// Use auxclick event over mousedown the latter doesn't seem to work. Note
|
||||
// that the regular click event doesn't fire for the middle mouse button.
|
||||
on(this.element, 'auxclick', event => {
|
||||
if (event.button === 1) {
|
||||
moveTextAreaUnderMouseCursor(event, this.textarea, this.selectionManager);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -648,8 +663,7 @@ Terminal.prototype.open = function(parent, focus) {
|
||||
this.viewportScrollArea.classList.add('xterm-scroll-area');
|
||||
this.viewportElement.appendChild(this.viewportScrollArea);
|
||||
|
||||
// Create the selection container. This needs to be added before the
|
||||
// rowContainer as the selection must be below the text.
|
||||
// Create the selection container.
|
||||
this.selectionContainer = document.createElement('div');
|
||||
this.selectionContainer.classList.add('xterm-selection');
|
||||
this.element.appendChild(this.selectionContainer);
|
||||
@@ -704,7 +718,17 @@ 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, this.lines, this.rowContainer, this.charMeasure);
|
||||
this.selectionManager.on('refresh', data => this.renderer.refreshSelection(data.start, data.end));
|
||||
this.selectionManager.on('refresh', data => {
|
||||
this.renderer.refreshSelection(data.start, data.end);
|
||||
});
|
||||
this.selectionManager.on('newselection', text => {
|
||||
// If there's a new selection, put it into the textarea, focus and select it
|
||||
// in order to register it as a selection on the OS. This event is fired
|
||||
// only on Linux to enable middle click to paste selection.
|
||||
this.textarea.value = text;
|
||||
this.textarea.focus();
|
||||
this.textarea.select();
|
||||
});
|
||||
this.on('scroll', () => this.selectionManager.refresh());
|
||||
this.viewportElement.addEventListener('scroll', () => this.selectionManager.refresh());
|
||||
|
||||
@@ -1306,15 +1330,27 @@ Terminal.prototype.writeln = function(data) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Attaches a custom keydown handler which is run before keys are processed, giving consumers of
|
||||
* xterm.js ultimate control as to what keys should be processed by the terminal and what keys
|
||||
* should not.
|
||||
* DEPRECATED: only for backward compatibility. Please use attachCustomKeyEventHandler() instead.
|
||||
* @param {function} customKeydownHandler The custom KeyboardEvent handler to attach. This is a
|
||||
* function that takes a KeyboardEvent, allowing consumers to stop propogation and/or prevent
|
||||
* the default action. The function returns whether the event should be processed by xterm.js.
|
||||
*/
|
||||
Terminal.prototype.attachCustomKeydownHandler = function(customKeydownHandler) {
|
||||
this.customKeydownHandler = customKeydownHandler;
|
||||
let message = 'attachCustomKeydownHandler() is DEPRECATED and will be removed soon. Please use attachCustomKeyEventHandler() instead.';
|
||||
console.warn(message);
|
||||
this.attachCustomKeyEventHandler(customKeydownHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches a custom key event handler which is run before keys are processed, giving consumers of
|
||||
* xterm.js ultimate control as to what keys should be processed by the terminal and what keys
|
||||
* should not.
|
||||
* @param {function} customKeypressHandler The custom KeyboardEvent handler to attach. This is a
|
||||
* function that takes a KeyboardEvent, allowing consumers to stop propogation and/or prevent
|
||||
* the default action. The function returns whether the event should be processed by xterm.js.
|
||||
*/
|
||||
Terminal.prototype.attachCustomKeyEventHandler = function(customKeyEventHandler) {
|
||||
this.customKeyEventHandler = customKeyEventHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1413,7 +1449,7 @@ Terminal.prototype.selectAll = function() {
|
||||
* @param {KeyboardEvent} ev The keydown event to be handled.
|
||||
*/
|
||||
Terminal.prototype.keyDown = function(ev) {
|
||||
if (this.customKeydownHandler && this.customKeydownHandler(ev) === false) {
|
||||
if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1778,6 +1814,10 @@ Terminal.prototype.setgCharset = function(g, charset) {
|
||||
Terminal.prototype.keyPress = function(ev) {
|
||||
var key;
|
||||
|
||||
if (this.customKeyEventHandler && this.customKeyEventHandler(ev) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.cancel(ev);
|
||||
|
||||
if (ev.charCode) {
|
||||
@@ -1803,7 +1843,7 @@ Terminal.prototype.keyPress = function(ev) {
|
||||
this.showCursor();
|
||||
this.handler(key);
|
||||
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2236,10 +2276,10 @@ Terminal.prototype.reverseIndex = function() {
|
||||
Terminal.prototype.reset = function() {
|
||||
this.options.rows = this.rows;
|
||||
this.options.cols = this.cols;
|
||||
var customKeydownHandler = this.customKeydownHandler;
|
||||
var customKeyEventHandler = this.customKeyEventHandler;
|
||||
var cursorBlinkInterval = this.cursorBlinkInterval;
|
||||
Terminal.call(this, this.options);
|
||||
this.customKeydownHandler = customKeydownHandler;
|
||||
this.customKeyEventHandler = customKeyEventHandler;
|
||||
this.cursorBlinkInterval = cursorBlinkInterval;
|
||||
this.refresh(0, this.rows - 1);
|
||||
this.viewport.syncScrollArea();
|
||||
|
||||
Reference in New Issue
Block a user