Skip to content

Commit 2447064

Browse files
authored
Merge pull request #2996 from jeanp413/fix-sync-textarea
Sync textarea with cursor
2 parents 85a2589 + 2b91bb8 commit 2447064

File tree

6 files changed

+32
-27
lines changed

6 files changed

+32
-27
lines changed

css/xterm.css

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@
5959
}
6060

6161
.xterm .xterm-helper-textarea {
62-
/*
63-
* HACK: to fix IE's blinking cursor
64-
* Move textarea out of the screen to the far left, so that the cursor is not visible.
65-
*/
62+
padding: 0;
63+
border: 0;
64+
margin: 0;
65+
/* Move textarea out of the screen to the far left, so that the cursor is not visible */
6666
position: absolute;
6767
opacity: 0;
6868
left: -9999em;

src/browser/Clipboard.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -70,25 +70,13 @@ export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextA
7070
const top = ev.clientY - pos.top - 10;
7171

7272
// Bring textarea at the cursor position
73-
textarea.style.position = 'absolute';
7473
textarea.style.width = '20px';
7574
textarea.style.height = '20px';
7675
textarea.style.left = `${left}px`;
7776
textarea.style.top = `${top}px`;
7877
textarea.style.zIndex = '1000';
7978

8079
textarea.focus();
81-
82-
// Reset the terminal textarea's styling
83-
// Timeout needs to be long enough for click event to be handled.
84-
setTimeout(() => {
85-
textarea.style.position = '';
86-
textarea.style.width = '';
87-
textarea.style.height = '';
88-
textarea.style.left = '';
89-
textarea.style.top = '';
90-
textarea.style.zIndex = '';
91-
}, 200);
9280
}
9381

9482
/**

src/browser/Terminal.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,25 @@ export class Terminal extends CoreTerminal implements ITerminal {
281281
this._onBlur.fire();
282282
}
283283

284+
private _syncTextArea(): void {
285+
if (!this.textarea || !this.buffer.isCursorInViewport || this._compositionHelper!.isComposing) {
286+
return;
287+
}
288+
289+
const cellHeight = Math.ceil(this._charSizeService!.height * this.optionsService.options.lineHeight);
290+
const cursorTop = this._bufferService.buffer.y * cellHeight;
291+
const cursorLeft = this._bufferService.buffer.x * this._charSizeService!.width;
292+
293+
// Sync the textarea to the exact position of the composition view so the IME knows where the
294+
// text is.
295+
this.textarea.style.left = cursorLeft + 'px';
296+
this.textarea.style.top = cursorTop + 'px';
297+
this.textarea.style.width = this._charSizeService!.width + 'px';
298+
this.textarea.style.height = cellHeight + 'px';
299+
this.textarea.style.lineHeight = cellHeight + 'px';
300+
this.textarea.style.zIndex = '-5';
301+
}
302+
284303
/**
285304
* Initialize default behavior
286305
*/
@@ -436,7 +455,10 @@ export class Terminal extends CoreTerminal implements ITerminal {
436455
this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea()));
437456
this.register(this.viewport);
438457

439-
this.register(this.onCursorMove(() => this._renderService!.onCursorMove()));
458+
this.register(this.onCursorMove(() => {
459+
this._renderService!.onCursorMove();
460+
this._syncTextArea();
461+
}));
440462
this.register(this.onResize(() => this._renderService!.onResize(this.cols, this.rows)));
441463
this.register(this.onBlur(() => this._renderService!.onBlur()));
442464
this.register(this.onFocus(() => this._renderService!.onFocus()));

src/browser/TestUtils.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ export class MockViewport implements IViewport {
309309
}
310310

311311
export class MockCompositionHelper implements ICompositionHelper {
312+
public get isComposing(): boolean {
313+
return false;
314+
}
312315
public compositionstart(): void {
313316
throw new Error('Method not implemented.');
314317
}

src/browser/Types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
8787
export type LineData = CharData[];
8888

8989
export interface ICompositionHelper {
90+
readonly isComposing: boolean;
9091
compositionstart(): void;
9192
compositionupdate(ev: CompositionEvent): void;
9293
compositionend(): void;

src/browser/input/CompositionHelper.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export class CompositionHelper {
2222
* IME. This variable determines whether the compositionText should be displayed on the UI.
2323
*/
2424
private _isComposing: boolean;
25+
public get isComposing(): boolean { return this._isComposing; }
2526

2627
/**
2728
* The position within the input textarea's value of the current composition.
@@ -118,7 +119,6 @@ export class CompositionHelper {
118119
private _finalizeComposition(waitForPropagation: boolean): void {
119120
this._compositionView.classList.remove('active');
120121
this._isComposing = false;
121-
this._clearTextareaPosition();
122122

123123
if (!waitForPropagation) {
124124
// Cancel any delayed composition send requests and send the input immediately.
@@ -218,13 +218,4 @@ export class CompositionHelper {
218218
setTimeout(() => this.updateCompositionElements(true), 0);
219219
}
220220
}
221-
222-
/**
223-
* Clears the textarea's position so that the cursor does not blink on IE.
224-
* @private
225-
*/
226-
private _clearTextareaPosition(): void {
227-
this._textarea.style.left = '';
228-
this._textarea.style.top = '';
229-
}
230221
}

0 commit comments

Comments
 (0)