mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Support blurred cursor
This commit is contained in:
@@ -35,6 +35,7 @@ export interface ITerminal extends IEventEmitter {
|
||||
options: ITerminalOptions;
|
||||
buffers: IBufferSet;
|
||||
buffer: IBuffer;
|
||||
isFocused: boolean;
|
||||
|
||||
/**
|
||||
* Emit the 'data' event and populate the given data.
|
||||
|
||||
@@ -412,6 +412,10 @@ export class Terminal extends EventEmitter implements ITerminal, IInputHandlingT
|
||||
this.textarea.focus();
|
||||
}
|
||||
|
||||
public get isFocused(): boolean {
|
||||
return document.activeElement === this.textarea;
|
||||
}
|
||||
|
||||
public setTheme(theme: ITheme): void {
|
||||
// TODO: Allow setting of theme before renderer is ready
|
||||
if (this.renderer) {
|
||||
|
||||
@@ -30,6 +30,8 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
|
||||
// TODO: Should this do anything?
|
||||
public onOptionsChanged(terminal: ITerminal): void {}
|
||||
public onBlur(terminal: ITerminal): void {}
|
||||
public onFocus(terminal: ITerminal): void {}
|
||||
public onCursorMove(terminal: ITerminal): void {}
|
||||
public onGridChanged(terminal: ITerminal, startRow: number, endRow: number): void {}
|
||||
public onSelectionChanged(terminal: ITerminal, start: [number, number], end: [number, number]): void {}
|
||||
@@ -58,7 +60,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._ctx.fillRect(startCol * this.scaledCharWidth, startRow * this.scaledCharHeight, colWidth * this.scaledCharWidth, colHeight * this.scaledCharHeight);
|
||||
}
|
||||
|
||||
protected fillBottomLineAtCell(x: number, y: number): void {
|
||||
protected drawBottomLineAtCell(x: number, y: number): void {
|
||||
this._ctx.fillRect(
|
||||
x * this.scaledCharWidth,
|
||||
(y + 1) * this.scaledCharHeight - window.devicePixelRatio - 1 /* Ensure it's drawn within the cell */,
|
||||
@@ -66,7 +68,7 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
window.devicePixelRatio);
|
||||
}
|
||||
|
||||
protected fillLeftLineAtCell(x: number, y: number): void {
|
||||
protected drawLeftLineAtCell(x: number, y: number): void {
|
||||
this._ctx.fillRect(
|
||||
x * this.scaledCharWidth,
|
||||
y * this.scaledCharHeight,
|
||||
@@ -74,6 +76,16 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this.scaledCharHeight);
|
||||
}
|
||||
|
||||
protected drawSquareAtCell(x: number, y: number, color: string): void {
|
||||
this._ctx.strokeStyle = color;
|
||||
this._ctx.lineWidth = window.devicePixelRatio;
|
||||
this._ctx.strokeRect(
|
||||
x * this.scaledCharWidth + window.devicePixelRatio / 2,
|
||||
y * this.scaledCharHeight + (window.devicePixelRatio / 2),
|
||||
this.scaledCharWidth - window.devicePixelRatio,
|
||||
this.scaledCharHeight - window.devicePixelRatio);
|
||||
}
|
||||
|
||||
protected clearAll(): void {
|
||||
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
private _state: [number, number];
|
||||
private _cursorRenderers: {[key: string]: (terminal: ITerminal, x: number, y: number, charData: CharData) => void};
|
||||
private _cursorBlinkStateManager: CursorBlinkStateManager;
|
||||
private _isFocused: boolean;
|
||||
|
||||
constructor(container: HTMLElement, zIndex: number, colors: IColorSet) {
|
||||
super(container, 'cursor', zIndex, colors);
|
||||
@@ -37,6 +38,20 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
}
|
||||
|
||||
public onBlur(terminal: ITerminal): void {
|
||||
if (this._cursorBlinkStateManager) {
|
||||
// this._cursorBlinkStateManager.pause();
|
||||
}
|
||||
terminal.refresh(terminal.buffer.y, terminal.buffer.y);
|
||||
}
|
||||
|
||||
public onFocus(terminal: ITerminal): void {
|
||||
if (this._cursorBlinkStateManager) {
|
||||
// this._cursorBlinkStateManager.resume();
|
||||
}
|
||||
terminal.refresh(terminal.buffer.y, terminal.buffer.y);
|
||||
}
|
||||
|
||||
public onOptionsChanged(terminal: ITerminal): void {
|
||||
if (terminal.options.cursorBlink) {
|
||||
if (!this._cursorBlinkStateManager) {
|
||||
@@ -69,11 +84,8 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
|
||||
private _render(terminal: ITerminal, triggeredByAnimationFrame: boolean): void {
|
||||
// TODO: Track blur/focus somehow, support unfocused cursor
|
||||
// Don't draw the cursor if it's hidden
|
||||
if (!terminal.cursorState ||
|
||||
terminal.cursorHidden ||
|
||||
(this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible)) {
|
||||
if (!terminal.cursorState || terminal.cursorHidden) {
|
||||
this._clearCursor();
|
||||
return;
|
||||
}
|
||||
@@ -87,6 +99,23 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
return;
|
||||
}
|
||||
|
||||
const charData = terminal.buffer.lines.get(cursorY)[terminal.buffer.x];
|
||||
|
||||
if (!terminal.isFocused) {
|
||||
this._clearCursor();
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this.colors.ansi[COLOR_CODES.WHITE];
|
||||
this._renderBlurCursor(terminal, terminal.buffer.x, viewportRelativeCursorY, charData);
|
||||
this._ctx.restore();
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't draw the cursor if it's blinking
|
||||
if (this._cursorBlinkStateManager && !this._cursorBlinkStateManager.isCursorVisible) {
|
||||
this._clearCursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._state) {
|
||||
// The cursor is already in the correct spot, don't redraw
|
||||
if (this._state[0] === terminal.buffer.x && this._state[1] === viewportRelativeCursorY) {
|
||||
@@ -95,7 +124,6 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
this._clearCursor();
|
||||
}
|
||||
|
||||
const charData = terminal.buffer.lines.get(cursorY)[terminal.buffer.x];
|
||||
this._ctx.save();
|
||||
this._ctx.fillStyle = this.colors.ansi[COLOR_CODES.WHITE];
|
||||
this._cursorRenderers[terminal.options.cursorStyle || 'block'](terminal, terminal.buffer.x, viewportRelativeCursorY, charData);
|
||||
@@ -111,7 +139,7 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
|
||||
private _renderBarCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {
|
||||
this.fillLeftLineAtCell(x, y);
|
||||
this.drawLeftLineAtCell(x, y);
|
||||
}
|
||||
|
||||
private _renderBlockCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {
|
||||
@@ -120,7 +148,12 @@ export class CursorRenderLayer extends BaseRenderLayer {
|
||||
}
|
||||
|
||||
private _renderUnderlineCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {
|
||||
this.fillBottomLineAtCell(x, y);
|
||||
this.drawBottomLineAtCell(x, y);
|
||||
}
|
||||
|
||||
private _renderBlurCursor(terminal: ITerminal, x: number, y: number, charData: CharData): void {
|
||||
// TODO: Support cursor colors
|
||||
this.drawSquareAtCell(x, y, this.colors.foreground);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export class ForegroundRenderLayer extends BaseRenderLayer {
|
||||
} else {
|
||||
this._ctx.fillStyle = this.colors.foreground;
|
||||
}
|
||||
this.fillBottomLineAtCell(x, y);
|
||||
this.drawBottomLineAtCell(x, y);
|
||||
}
|
||||
|
||||
this.drawChar(terminal, char, code, x, y, fg);
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import { ITerminal, ITerminalOptions } from '../Interfaces';
|
||||
|
||||
export interface IRenderLayer {
|
||||
/**
|
||||
* Called when the terminal loses focus.
|
||||
*/
|
||||
onBlur(terminal: ITerminal): void;
|
||||
|
||||
/**
|
||||
* * Called when the terminal gets focus.
|
||||
*/
|
||||
onFocus(terminal: ITerminal): void;
|
||||
|
||||
/**
|
||||
* Called when the cursor is moved.
|
||||
*/
|
||||
|
||||
@@ -57,6 +57,14 @@ export class Renderer {
|
||||
this._renderLayers.forEach(l => l.resize(this._terminal, width, height, true));
|
||||
}
|
||||
|
||||
public onBlur(): void {
|
||||
this._renderLayers.forEach(l => l.onBlur(this._terminal));
|
||||
}
|
||||
|
||||
public onFocus(): void {
|
||||
this._renderLayers.forEach(l => l.onFocus(this._terminal));
|
||||
}
|
||||
|
||||
public onSelectionChanged(start: [number, number], end: [number, number]): void {
|
||||
this._renderLayers.forEach(l => l.onSelectionChanged(this._terminal, start, end));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user