diff --git a/src/Buffer.ts b/src/Buffer.ts index 101244f1..6931eea4 100644 --- a/src/Buffer.ts +++ b/src/Buffer.ts @@ -52,6 +52,12 @@ export class Buffer implements IBuffer { return this._hasScrollback && this.lines.maxLength > this._terminal.rows; } + public get isCursorInViewport(): boolean { + const absoluteY = this.ybase + this.y; + const relativeY = absoluteY - this.ydisp; + return (relativeY >= 0 && relativeY < this._terminal.rows); + } + /** * Gets the correct buffer length based on the rows provided, the terminal's * scrollback and whether this buffer is flagged to have scrollback or not. diff --git a/src/CompositionHelper.ts b/src/CompositionHelper.ts index 223a90e7..345041d6 100644 --- a/src/CompositionHelper.ts +++ b/src/CompositionHelper.ts @@ -63,6 +63,7 @@ export class CompositionHelper { * @param {CompositionEvent} ev The event. */ public compositionupdate(ev: CompositionEvent): void { + console.log('compositionupdate'); this.compositionView.textContent = ev.data; this.updateCompositionElements(); setTimeout(() => { @@ -193,26 +194,26 @@ export class CompositionHelper { if (!this.isComposing) { return; } - const cursor = this.terminal.element.querySelector('.terminal-cursor'); - if (cursor) { - // Take .xterm-rows offsetTop into account as well in case it's positioned absolutely within - // the .xterm element. - const xtermRows = this.terminal.element.querySelector('.xterm-rows'); - const cursorTop = xtermRows.offsetTop + cursor.offsetTop; - this.compositionView.style.left = cursor.offsetLeft + 'px'; + 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 = cursor.offsetHeight + 'px'; - this.compositionView.style.lineHeight = cursor.offsetHeight + '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 = cursor.offsetLeft + 'px'; + 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) { setTimeout(() => this.updateCompositionElements(true), 0); } diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 2b4909c9..7ebf6983 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -145,6 +145,7 @@ export interface IBuffer { scrollTop: number; savedY: number; savedX: number; + isCursorInViewport: boolean; translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string; nextStop(x?: number): number; prevStop(x?: number): number; diff --git a/src/xterm.css b/src/xterm.css index 0ddfe8b5..df3aff59 100644 --- a/src/xterm.css +++ b/src/xterm.css @@ -52,6 +52,11 @@ .terminal .xterm-helpers { position: absolute; top: 0; + /** + * The z-index of the helpers must be higher than the canvases in order for + * IMEs to appear on top. + */ + z-index: 10; } .terminal .xterm-helper-textarea {