Merge remote-tracking branch 'origin/master' into 426_alt_scroll

This commit is contained in:
Daniel Imms
2018-03-08 09:28:02 -08:00
7 changed files with 255 additions and 255 deletions
+61 -61
View File
@@ -20,43 +20,43 @@ export class CompositionHelper {
* Whether input composition is currently happening, eg. via a mobile keyboard, speech input or
* IME. This variable determines whether the compositionText should be displayed on the UI.
*/
private isComposing: boolean;
private _isComposing: boolean;
/**
* The position within the input textarea's value of the current composition.
*/
private compositionPosition: IPosition;
private _compositionPosition: IPosition;
/**
* Whether a composition is in the process of being sent, setting this to false will cancel any
* in-progress composition.
*/
private isSendingComposition: boolean;
private _isSendingComposition: boolean;
/**
* Creates a new CompositionHelper.
* @param textarea The textarea that xterm uses for input.
* @param compositionView The element to display the in-progress composition in.
* @param terminal The Terminal to forward the finished composition to.
* @param _textarea The textarea that xterm uses for input.
* @param _compositionView The element to display the in-progress composition in.
* @param _terminal The Terminal to forward the finished composition to.
*/
constructor(
private textarea: HTMLTextAreaElement,
private compositionView: HTMLElement,
private terminal: ITerminal
private _textarea: HTMLTextAreaElement,
private _compositionView: HTMLElement,
private _terminal: ITerminal
) {
this.isComposing = false;
this.isSendingComposition = false;
this.compositionPosition = { start: null, end: null };
this._isComposing = false;
this._isSendingComposition = false;
this._compositionPosition = { start: null, end: null };
}
/**
* Handles the compositionstart event, activating the composition view.
*/
public compositionstart(): void {
this.isComposing = true;
this.compositionPosition.start = this.textarea.value.length;
this.compositionView.textContent = '';
this.compositionView.classList.add('active');
this._isComposing = true;
this._compositionPosition.start = this._textarea.value.length;
this._compositionView.textContent = '';
this._compositionView.classList.add('active');
}
/**
@@ -64,10 +64,10 @@ export class CompositionHelper {
* @param {CompositionEvent} ev The event.
*/
public compositionupdate(ev: CompositionEvent): void {
this.compositionView.textContent = ev.data;
this._compositionView.textContent = ev.data;
this.updateCompositionElements();
setTimeout(() => {
this.compositionPosition.end = this.textarea.value.length;
this._compositionPosition.end = this._textarea.value.length;
}, 0);
}
@@ -76,7 +76,7 @@ export class CompositionHelper {
* the handler.
*/
public compositionend(): void {
this.finalizeComposition(true);
this._finalizeComposition(true);
}
/**
@@ -85,7 +85,7 @@ export class CompositionHelper {
* @return Whether the Terminal should continue processing the keydown event.
*/
public keydown(ev: KeyboardEvent): boolean {
if (this.isComposing || this.isSendingComposition) {
if (this._isComposing || this._isSendingComposition) {
if (ev.keyCode === 229) {
// Continue composing if the keyCode is the "composition character"
return false;
@@ -95,14 +95,14 @@ export class CompositionHelper {
} else {
// Finish composition immediately. This is mainly here for the case where enter is
// pressed and the handler needs to be triggered before the command is executed.
this.finalizeComposition(false);
this._finalizeComposition(false);
}
}
if (ev.keyCode === 229) {
// If the "composition character" is used but gets to this point it means a non-composition
// character (eg. numbers and punctuation) was pressed when the IME was active.
this.handleAnyTextareaChanges();
this._handleAnyTextareaChanges();
return false;
}
@@ -117,22 +117,22 @@ export class CompositionHelper {
* compositionend event is triggered, such as enter, so that the composition is send before
* the command is executed.
*/
private finalizeComposition(waitForPropogation: boolean): void {
this.compositionView.classList.remove('active');
this.isComposing = false;
this.clearTextareaPosition();
private _finalizeComposition(waitForPropogation: boolean): void {
this._compositionView.classList.remove('active');
this._isComposing = false;
this._clearTextareaPosition();
if (!waitForPropogation) {
// Cancel any delayed composition send requests and send the input immediately.
this.isSendingComposition = false;
const input = this.textarea.value.substring(this.compositionPosition.start, this.compositionPosition.end);
this.terminal.handler(input);
this._isSendingComposition = false;
const input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end);
this._terminal.handler(input);
} else {
// Make a deep copy of the composition position here as a new compositionstart event may
// fire before the setTimeout executes.
const currentCompositionPosition = {
start: this.compositionPosition.start,
end: this.compositionPosition.end,
start: this._compositionPosition.start,
end: this._compositionPosition.end,
};
// Since composition* events happen before the changes take place in the textarea on most
@@ -143,22 +143,22 @@ export class CompositionHelper {
// - The last compositionupdate event's data property does not always accurately describe
// the character, a counter example being Korean where an ending consonsant can move to
// the following character if the following input is a vowel.
this.isSendingComposition = true;
this._isSendingComposition = true;
setTimeout(() => {
// Ensure that the input has not already been sent
if (this.isSendingComposition) {
this.isSendingComposition = false;
if (this._isSendingComposition) {
this._isSendingComposition = false;
let input;
if (this.isComposing) {
if (this._isComposing) {
// Use the end position to get the string if a new composition has started.
input = this.textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
input = this._textarea.value.substring(currentCompositionPosition.start, currentCompositionPosition.end);
} else {
// Don't use the end position here in order to pick up any characters after the
// composition has finished, for example when typing a non-composition character
// (eg. 2) after a composition character.
input = this.textarea.value.substring(currentCompositionPosition.start);
input = this._textarea.value.substring(currentCompositionPosition.start);
}
this.terminal.handler(input);
this._terminal.handler(input);
}
}, 0);
}
@@ -170,15 +170,15 @@ export class CompositionHelper {
* character" (229) is triggered, in order to allow non-composition text to be entered when an
* IME is active.
*/
private handleAnyTextareaChanges(): void {
const oldValue = this.textarea.value;
private _handleAnyTextareaChanges(): void {
const oldValue = this._textarea.value;
setTimeout(() => {
// Ignore if a composition has started since the timeout
if (!this.isComposing) {
const newValue = this.textarea.value;
if (!this._isComposing) {
const newValue = this._textarea.value;
const diff = newValue.replace(oldValue, '');
if (diff.length > 0) {
this.terminal.handler(diff);
this._terminal.handler(diff);
}
}
}, 0);
@@ -191,27 +191,27 @@ export class CompositionHelper {
* necessary as the IME events across browsers are not consistently triggered.
*/
public updateCompositionElements(dontRecurse?: boolean): void {
if (!this.isComposing) {
if (!this._isComposing) {
return;
}
if (this.terminal.buffer.isCursorInViewport) {
const cellHeight = Math.ceil(this.terminal.charMeasure.height * this.terminal.options.lineHeight);
const cursorTop = this.terminal.buffer.y * cellHeight;
const cursorLeft = this.terminal.buffer.x * this.terminal.charMeasure.width;
if (this._terminal.buffer.isCursorInViewport) {
const cellHeight = Math.ceil(this._terminal.charMeasure.height * this._terminal.options.lineHeight);
const cursorTop = this._terminal.buffer.y * cellHeight;
const cursorLeft = this._terminal.buffer.x * this._terminal.charMeasure.width;
this.compositionView.style.left = cursorLeft + 'px';
this.compositionView.style.top = cursorTop + 'px';
this.compositionView.style.height = cellHeight + 'px';
this.compositionView.style.lineHeight = cellHeight + 'px';
this._compositionView.style.left = cursorLeft + 'px';
this._compositionView.style.top = cursorTop + 'px';
this._compositionView.style.height = cellHeight + 'px';
this._compositionView.style.lineHeight = cellHeight + 'px';
// Sync the textarea to the exact position of the composition view so the IME knows where the
// text is.
const compositionViewBounds = this.compositionView.getBoundingClientRect();
this.textarea.style.left = cursorLeft + 'px';
this.textarea.style.top = cursorTop + 'px';
this.textarea.style.width = compositionViewBounds.width + 'px';
this.textarea.style.height = compositionViewBounds.height + 'px';
this.textarea.style.lineHeight = compositionViewBounds.height + 'px';
const compositionViewBounds = this._compositionView.getBoundingClientRect();
this._textarea.style.left = cursorLeft + 'px';
this._textarea.style.top = cursorTop + 'px';
this._textarea.style.width = compositionViewBounds.width + 'px';
this._textarea.style.height = compositionViewBounds.height + 'px';
this._textarea.style.lineHeight = compositionViewBounds.height + 'px';
}
if (!dontRecurse) {
@@ -223,8 +223,8 @@ export class CompositionHelper {
* Clears the textarea's position so that the cursor does not blink on IE.
* @private
*/
private clearTextareaPosition(): void {
this.textarea.style.left = '';
this.textarea.style.top = '';
private _clearTextareaPosition(): void {
this._textarea.style.left = '';
this._textarea.style.top = '';
}
}
+3 -3
View File
@@ -28,7 +28,7 @@ export class SoundManager implements ISoundManager {
if (this._audioContext) {
const bellAudioSource = this._audioContext.createBufferSource();
const context = this._audioContext;
this._audioContext.decodeAudioData(this.base64ToArrayBuffer(this.removeMimeType(this._terminal.options.bellSound)), (buffer) => {
this._audioContext.decodeAudioData(this._base64ToArrayBuffer(this._removeMimeType(this._terminal.options.bellSound)), (buffer) => {
bellAudioSource.buffer = buffer;
bellAudioSource.connect(context.destination);
bellAudioSource.start(0);
@@ -38,7 +38,7 @@ export class SoundManager implements ISoundManager {
}
}
private base64ToArrayBuffer(base64: string): ArrayBuffer {
private _base64ToArrayBuffer(base64: string): ArrayBuffer {
const binaryString = window.atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
@@ -50,7 +50,7 @@ export class SoundManager implements ISoundManager {
return bytes.buffer;
}
private removeMimeType(dataURI: string): string {
private _removeMimeType(dataURI: string): string {
// Split the input to get the mime-type and the data itself
const splitUri = dataURI.split(',');
+1 -1
View File
@@ -125,7 +125,7 @@ if (os.platform() !== 'win32') {
// Perform a synchronous .write(data)
xterm.writeBuffer.push(fromPty);
xterm.innerWrite();
xterm._innerWrite();
let fromEmulator = terminalToString(xterm);
console.log = CONSOLE_LOG;
+2 -2
View File
@@ -29,11 +29,11 @@ describe('term.js addons', () => {
term.refresh = () => {};
(<any>term).renderer = new MockRenderer();
term.viewport = new MockViewport();
(<any>term).compositionHelper = new MockCompositionHelper();
(<any>term)._compositionHelper = new MockCompositionHelper();
// Force synchronous writes
term.write = (data) => {
term.writeBuffer.push(data);
(<any>term).innerWrite();
(<any>term)._innerWrite();
};
(<any>term).element = {
classList: {
+136 -136
View File
File diff suppressed because it is too large Load Diff
+48 -48
View File
@@ -15,11 +15,11 @@ const FALLBACK_SCROLL_BAR_WIDTH = 15;
*/
export class Viewport implements IViewport {
public scrollBarWidth: number = 0;
private currentRowHeight: number = 0;
private lastRecordedBufferLength: number = 0;
private lastRecordedViewportHeight: number = 0;
private lastRecordedBufferHeight: number = 0;
private lastTouchY: number;
private _currentRowHeight: number = 0;
private _lastRecordedBufferLength: number = 0;
private _lastRecordedViewportHeight: number = 0;
private _lastRecordedBufferHeight: number = 0;
private _lastTouchY: number;
// Stores a partial line amount when scrolling, this is used to keep track of how much of a line
// is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a
@@ -28,43 +28,43 @@ export class Viewport implements IViewport {
/**
* Creates a new Viewport.
* @param terminal The terminal this viewport belongs to.
* @param viewportElement The DOM element acting as the viewport.
* @param scrollArea The DOM element acting as the scroll area.
* @param charMeasure A DOM element used to measure the character size of. the terminal.
* @param _terminal The terminal this viewport belongs to.
* @param _viewportElement The DOM element acting as the viewport.
* @param _scrollArea The DOM element acting as the scroll area.
* @param _charMeasure A DOM element used to measure the character size of. the terminal.
*/
constructor(
private terminal: ITerminal,
private viewportElement: HTMLElement,
private scrollArea: HTMLElement,
private charMeasure: CharMeasure
private _terminal: ITerminal,
private _viewportElement: HTMLElement,
private _scrollArea: HTMLElement,
private _charMeasure: CharMeasure
) {
// Measure the width of the scrollbar. If it is 0 we can assume it's an OSX overlay scrollbar.
// Unfortunately the overlay scrollbar would be hidden underneath the screen element in that case,
// therefore we account for a standard amount to make it visible
this.scrollBarWidth = (this.viewportElement.offsetWidth - this.scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH;
this.viewportElement.addEventListener('scroll', this.onScroll.bind(this));
this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH;
this._viewportElement.addEventListener('scroll', this._onScroll.bind(this));
// Perform this async to ensure the CharMeasure is ready.
setTimeout(() => this.syncScrollArea(), 0);
}
public onThemeChanged(colors: IColorSet): void {
this.viewportElement.style.backgroundColor = colors.background;
this._viewportElement.style.backgroundColor = colors.background;
}
/**
* Refreshes row height, setting line-height, viewport height and scroll area height if
* necessary.
*/
private refresh(): void {
if (this.charMeasure.height > 0) {
this.currentRowHeight = this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio;
this.lastRecordedViewportHeight = this.viewportElement.offsetHeight;
const newBufferHeight = Math.round(this.currentRowHeight * this.lastRecordedBufferLength) + (this.lastRecordedViewportHeight - this.terminal.renderer.dimensions.canvasHeight);
if (this.lastRecordedBufferHeight !== newBufferHeight) {
this.lastRecordedBufferHeight = newBufferHeight;
this.scrollArea.style.height = this.lastRecordedBufferHeight + 'px';
private _refresh(): void {
if (this._charMeasure.height > 0) {
this._currentRowHeight = this._terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio;
this._lastRecordedViewportHeight = this._viewportElement.offsetHeight;
const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._terminal.renderer.dimensions.canvasHeight);
if (this._lastRecordedBufferHeight !== newBufferHeight) {
this._lastRecordedBufferHeight = newBufferHeight;
this._scrollArea.style.height = this._lastRecordedBufferHeight + 'px';
}
}
}
@@ -73,24 +73,24 @@ export class Viewport implements IViewport {
* Updates dimensions and synchronizes the scroll area if necessary.
*/
public syncScrollArea(): void {
if (this.lastRecordedBufferLength !== this.terminal.buffer.lines.length) {
if (this._lastRecordedBufferLength !== this._terminal.buffer.lines.length) {
// If buffer height changed
this.lastRecordedBufferLength = this.terminal.buffer.lines.length;
this.refresh();
} else if (this.lastRecordedViewportHeight !== (<any>this.terminal).renderer.dimensions.canvasHeight) {
this._lastRecordedBufferLength = this._terminal.buffer.lines.length;
this._refresh();
} else if (this._lastRecordedViewportHeight !== (<any>this._terminal).renderer.dimensions.canvasHeight) {
// If viewport height changed
this.refresh();
this._refresh();
} else {
// If size has changed, refresh viewport
if (this.terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this.currentRowHeight) {
this.refresh();
if (this._terminal.renderer.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) {
this._refresh();
}
}
// Sync scrollTop
const scrollTop = this.terminal.buffer.ydisp * this.currentRowHeight;
if (this.viewportElement.scrollTop !== scrollTop) {
this.viewportElement.scrollTop = scrollTop;
const scrollTop = this._terminal.buffer.ydisp * this._currentRowHeight;
if (this._viewportElement.scrollTop !== scrollTop) {
this._viewportElement.scrollTop = scrollTop;
}
}
@@ -99,16 +99,16 @@ export class Viewport implements IViewport {
* terminal to scroll to it.
* @param ev The scroll event.
*/
private onScroll(ev: Event): void {
private _onScroll(ev: Event): void {
// Don't attempt to scroll if the element is not visible, otherwise scrollTop will be corrupt
// which causes the terminal to scroll the buffer to the top
if (!this.viewportElement.offsetParent) {
if (!this._viewportElement.offsetParent) {
return;
}
const newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight);
const diff = newRow - this.terminal.buffer.ydisp;
this.terminal.scrollLines(diff, true);
const newRow = Math.round(this._viewportElement.scrollTop / this._currentRowHeight);
const diff = newRow - this._terminal.buffer.ydisp;
this._terminal.scrollLines(diff, true);
}
/**
@@ -122,7 +122,7 @@ export class Viewport implements IViewport {
if (amount === 0) {
return;
}
this.viewportElement.scrollTop += amount;
this._viewportElement.scrollTop += amount;
// Prevent the page from scrolling when the terminal scrolls
ev.preventDefault();
}
@@ -136,9 +136,9 @@ export class Viewport implements IViewport {
// Fallback to WheelEvent.DOM_DELTA_PIXEL
let amount = ev.deltaY;
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
amount *= this.currentRowHeight;
amount *= this._currentRowHeight;
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
amount *= this.currentRowHeight * this.terminal.rows;
amount *= this._currentRowHeight * this._terminal.rows;
}
return amount;
}
@@ -157,12 +157,12 @@ export class Viewport implements IViewport {
// Fallback to WheelEvent.DOM_DELTA_LINE
let amount = ev.deltaY;
if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
amount /= this.currentRowHeight + 0.0; // Prevent integer division
amount /= this._currentRowHeight + 0.0; // Prevent integer division
this._wheelPartialScroll += amount;
amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1);
this._wheelPartialScroll %= 1;
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
amount *= this.terminal.rows;
amount *= this._terminal.rows;
}
return amount;
}
@@ -172,7 +172,7 @@ export class Viewport implements IViewport {
* @param ev The touch event.
*/
public onTouchStart(ev: TouchEvent): void {
this.lastTouchY = ev.touches[0].pageY;
this._lastTouchY = ev.touches[0].pageY;
}
/**
@@ -180,12 +180,12 @@ export class Viewport implements IViewport {
* @param ev The touch event.
*/
public onTouchMove(ev: TouchEvent): void {
let deltaY = this.lastTouchY - ev.touches[0].pageY;
this.lastTouchY = ev.touches[0].pageY;
let deltaY = this._lastTouchY - ev.touches[0].pageY;
this._lastTouchY = ev.touches[0].pageY;
if (deltaY === 0) {
return;
}
this.viewportElement.scrollTop += deltaY;
this._viewportElement.scrollTop += deltaY;
ev.preventDefault();
}
}
+4 -4
View File
@@ -237,7 +237,7 @@ class CursorBlinkStateManager {
constructor(
terminal: ITerminal,
private renderCallback: () => void
private _renderCallback: () => void
) {
this.isCursorVisible = true;
if (terminal.isFocused) {
@@ -272,7 +272,7 @@ class CursorBlinkStateManager {
this.isCursorVisible = true;
if (!this._animationFrame) {
this._animationFrame = window.requestAnimationFrame(() => {
this.renderCallback();
this._renderCallback();
this._animationFrame = null;
});
}
@@ -303,7 +303,7 @@ class CursorBlinkStateManager {
// Hide the cursor
this.isCursorVisible = false;
this._animationFrame = window.requestAnimationFrame(() => {
this.renderCallback();
this._renderCallback();
this._animationFrame = null;
});
@@ -322,7 +322,7 @@ class CursorBlinkStateManager {
// Invert visibility and render
this.isCursorVisible = !this.isCursorVisible;
this._animationFrame = window.requestAnimationFrame(() => {
this.renderCallback();
this._renderCallback();
this._animationFrame = null;
});
}, BLINK_INTERVAL);