From 482dad3036e35d39f6a1a6d3765ab03005b5a31f Mon Sep 17 00:00:00 2001 From: tisilent Date: Thu, 10 Aug 2023 16:11:04 +0800 Subject: [PATCH 1/6] Add cursorInactiveStyle option --- .../src/CursorRenderLayer.ts | 9 +- addons/xterm-addon-webgl/src/WebglRenderer.ts | 15 +++- demo/client.ts | 1 + src/browser/renderer/dom/DomRenderer.ts | 17 +++- .../dom/DomRendererRowFactory.test.ts | 85 ++++++++++--------- .../renderer/dom/DomRendererRowFactory.ts | 23 ++++- src/common/Types.d.ts | 2 + src/common/services/OptionsService.ts | 1 + src/common/services/Services.ts | 3 +- typings/xterm.d.ts | 5 ++ 10 files changed, 115 insertions(+), 46 deletions(-) diff --git a/addons/xterm-addon-canvas/src/CursorRenderLayer.ts b/addons/xterm-addon-canvas/src/CursorRenderLayer.ts index 19b07b5f..b35cdbbc 100644 --- a/addons/xterm-addon-canvas/src/CursorRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/CursorRenderLayer.ts @@ -150,7 +150,14 @@ export class CursorRenderLayer extends BaseRenderLayer { this._ctx.save(); this._ctx.fillStyle = this._themeService.colors.cursor.css; const cursorStyle = this._optionsService.rawOptions.cursorStyle; - this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell); + if (this._optionsService.rawOptions.cursorInactiveStyle === 'outline') { + this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell); + } else if (this._optionsService.rawOptions.cursorInactiveStyle === 'line') { + this._cursorRenderers['bar'](cursorX, viewportRelativeCursorY, this._cell); + } else if (this._optionsService.rawOptions.cursorInactiveStyle === 'underline') { + this._cursorRenderers['underline'](cursorX, viewportRelativeCursorY, this._cell); + } else { + } this._ctx.restore(); this._state.x = cursorX; this._state.y = viewportRelativeCursorY; diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index b74aada9..f7d190ee 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -461,7 +461,7 @@ export class WebglRenderer extends Disposable implements IRenderer { y: this._terminal.buffer.active.cursorY, width: cell.getWidth(), style: this._coreBrowserService.isFocused ? - (terminal.options.cursorStyle || 'block') : 'blur', + (terminal.options.cursorStyle || 'block') : this._getInactiveCursorStyle(terminal.options.cursorInactiveStyle), cursorWidth: terminal.options.cursorWidth, dpr: this._devicePixelRatio }; @@ -600,6 +600,19 @@ export class WebglRenderer extends Disposable implements IRenderer { const cursorY = this._terminal.buffer.active.cursorY; this._onRequestRedraw.fire({ start: cursorY, end: cursorY }); } + + private _getInactiveCursorStyle(cursorInactiveStyle: 'outline' | 'line' | 'underline' | 'none'): string { + if (cursorInactiveStyle === 'outline') { + return 'blur'; + } + if (cursorInactiveStyle === 'line') { + return 'bar'; + } + if (cursorInactiveStyle === 'underline'){ + return 'underline'; + } + return 'block'; + } } // TODO: Share impl with core diff --git a/demo/client.ts b/demo/client.ts index a7c70e5b..b5c67208 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -419,6 +419,7 @@ function initOptions(term: TerminalType): void { ]; const stringOptions = { cursorStyle: ['block', 'underline', 'bar'], + cursorInactiveStyle: ['outline', 'line', 'underline', 'none'], fastScrollModifier: ['none', 'alt', 'ctrl', 'shift'], fontFamily: null, fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index cd854696..be703ebe 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -197,13 +197,18 @@ export class DomRenderer extends Disposable implements IRenderer { `}`; // Cursor styles += - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} ,` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} ,` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} ` + - `{` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_INACTIVE_STYLE_OUTLINE_CLASS} {` + ` outline: 1px solid ${colors.cursor.css};` + ` outline-offset: -1px;` + `}` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_INACTIVE_STYLE_LINE_CLASS} {` + + ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` + + `}` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_INACTIVE_STYLE_UNDERLINE_CLASS} {` + + ` border-bottom: 1px ${colors.cursor.css};` + + ` border-bottom-style: solid;` + + ` height: calc(100% - 1px);` + + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}:not(.${RowCss.CURSOR_STYLE_BLOCK_CLASS}) {` + ` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` + `}` + @@ -408,6 +413,7 @@ export class DomRenderer extends Disposable implements IRenderer { const cursorX = Math.min(buffer.x, this._bufferService.cols - 1); const cursorBlink = this._optionsService.rawOptions.cursorBlink; const cursorStyle = this._optionsService.rawOptions.cursorStyle; + const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle; for (let y = start; y <= end; y++) { const row = y + buffer.ydisp; @@ -422,6 +428,7 @@ export class DomRenderer extends Disposable implements IRenderer { row, row === cursorAbsoluteY, cursorStyle, + cursorInactiveStyle, cursorX, cursorBlink, this.dimensions.css.cell.width, @@ -474,6 +481,7 @@ export class DomRenderer extends Disposable implements IRenderer { const cursorX = Math.min(buffer.x, cols - 1); const cursorBlink = this._optionsService.rawOptions.cursorBlink; const cursorStyle = this._optionsService.rawOptions.cursorStyle; + const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle; // refresh rows within link range for (let i = y; i <= y2; ++i) { @@ -489,6 +497,7 @@ export class DomRenderer extends Disposable implements IRenderer { row, row === cursorAbsoluteY, cursorStyle, + cursorInactiveStyle, cursorX, cursorBlink, this.dimensions.css.cell.width, diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index 455a401d..e95952f2 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -39,7 +39,7 @@ describe('DomRendererRowFactory', () => { describe('createRow', () => { it('should not create anything for an empty row', () => { - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), '' ); @@ -50,7 +50,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, '語', 2, '語'.charCodeAt(0)])); // There should be no element for the following "empty" cell lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, '', 0, 0])); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), '' ); @@ -58,7 +58,7 @@ describe('DomRendererRowFactory', () => { it('should add class for cursor and cursor style', () => { for (const style of ['block', 'bar', 'underline']) { - const spans = rowFactory.createRow(lineData, 0, true, style, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, true, style, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), ` ` ); @@ -66,18 +66,27 @@ describe('DomRendererRowFactory', () => { }); it('should add class for cursor blink', () => { - const spans = rowFactory.createRow(lineData, 0, true, 'block', 0, true, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, true, 'block', undefined, 0, true, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), ` ` ); }); + it('should add class for inactive cursor', () => { + for (const inactiveStyle of ['outline', 'line', 'underline', 'none']){ + const spans = rowFactory.createRow(lineData, 0, true, 'block', inactiveStyle, 0, false, 5, EMPTY_WIDTH, -1, -1); + assert.equal(extractHtml(spans), + ` ` + ); + } + }); + describe('attributes', () => { it('should add class for bold', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.BOLD; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -87,7 +96,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.ITALIC; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -97,7 +106,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.DIM; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -110,7 +119,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.SINGLE; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -121,7 +130,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.DOUBLE; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -132,7 +141,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.CURLY; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -143,7 +152,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.DOTTED; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -154,7 +163,7 @@ describe('DomRendererRowFactory', () => { cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.HAS_EXTENDED; cell.extended.underlineStyle = UnderlineStyle.DASHED; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -165,7 +174,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.bg = DEFAULT_ATTR_DATA.bg | BgFlags.OVERLINE; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -175,7 +184,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.fg = DEFAULT_ATTR_DATA.fg | FgFlags.STRIKETHROUGH; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -188,7 +197,7 @@ describe('DomRendererRowFactory', () => { cell.fg &= ~Attributes.PCOLOR_MASK; cell.fg |= i; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), `a` ); @@ -202,7 +211,7 @@ describe('DomRendererRowFactory', () => { cell.bg &= ~Attributes.PCOLOR_MASK; cell.bg |= i; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), `a` ); @@ -214,7 +223,7 @@ describe('DomRendererRowFactory', () => { cell.fg |= Attributes.CM_P16 | 2 | FgFlags.INVERSE; cell.bg |= Attributes.CM_P16 | 1; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -225,7 +234,7 @@ describe('DomRendererRowFactory', () => { cell.fg |= FgFlags.INVERSE; cell.bg |= Attributes.CM_P16 | 1; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -235,7 +244,7 @@ describe('DomRendererRowFactory', () => { const cell = CellData.fromCharData([0, 'a', 1, 'a'.charCodeAt(0)]); cell.fg |= Attributes.CM_P16 | 1 | FgFlags.INVERSE; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -248,7 +257,7 @@ describe('DomRendererRowFactory', () => { cell.fg &= ~Attributes.PCOLOR_MASK; cell.fg |= i; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), `a` ); @@ -260,7 +269,7 @@ describe('DomRendererRowFactory', () => { cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3; cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -271,7 +280,7 @@ describe('DomRendererRowFactory', () => { cell.fg |= Attributes.CM_RGB | 1 << 16 | 2 << 8 | 3 | FgFlags.INVERSE; cell.bg |= Attributes.CM_RGB | 4 << 16 | 5 << 8 | 6; lineData.setCell(0, cell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'a' ); @@ -283,7 +292,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); rowFactory.handleSelectionChanged([1, 0], [2, 0], false); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'ab' ); @@ -291,7 +300,7 @@ describe('DomRendererRowFactory', () => { it('should force whitespace cells to be rendered above the background', () => { lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); rowFactory.handleSelectionChanged([0, 0], [2, 0], false); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), ' a' ); @@ -308,7 +317,7 @@ describe('DomRendererRowFactory', () => { }); it('should not create anything for an empty row', () => { - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), '' ); @@ -318,7 +327,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'c', 1, 'c'.charCodeAt(0)])); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'abc' ); @@ -329,7 +338,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(0, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, '€', 1, '€'.charCodeAt(0)])); lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'c', 1, 'c'.charCodeAt(0)])); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'ac' ); @@ -344,7 +353,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(1, aColor1); lineData.setCell(2, bColor2); lineData.setCell(3, bColor2); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'aabb' ); @@ -356,7 +365,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'X', 1, 'X'.charCodeAt(0)])); lineData.setCell(3, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); - const spans = rowFactory.createRow(lineData, 0, true, undefined, 2, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, true, undefined, undefined, 2, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'aaXbb' ); @@ -369,7 +378,7 @@ describe('DomRendererRowFactory', () => { nullCell.bg = Attributes.CM_P16 | 2; lineData.setCell(3, nullCell); lineData.setCell(4, nullCell); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), ' ' ); @@ -379,23 +388,23 @@ describe('DomRendererRowFactory', () => { const nullCell = lineData.loadCell(0, new CellData()); nullCell.bg = Attributes.CM_P16 | 1; lineData.setCell(0, nullCell); - let spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + let spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), ' ' ); lineData.setCell(1, nullCell); - spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), ' ' ); lineData.setCell(2, nullCell); lineData.setCell(3, nullCell); - spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), ' ' ); lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); - spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), ' a' ); @@ -410,7 +419,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'c', 1, 'c'.charCodeAt(0)])); lineData.setCell(3, CellData.fromCharData([DEFAULT_ATTR, '語', 2, 'c'.charCodeAt(0)])); lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, '𝄞', 1, 'c'.charCodeAt(0)])); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -1, -1); assert.equal(extractHtml(spans), 'ac語𝄞' ); @@ -424,7 +433,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'x', 1, 'x'.charCodeAt(0)])); lineData.setCell(5, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); lineData.setCell(6, CellData.fromCharData([DEFAULT_ATTR, 'b', 1, 'b'.charCodeAt(0)])); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, 2, 4); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, 2, 4); assert.equal(extractHtml(spans), 'aaxxxbb' ); @@ -435,7 +444,7 @@ describe('DomRendererRowFactory', () => { lineData.setCell(1, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); lineData.setCell(2, CellData.fromCharData([DEFAULT_ATTR, 'x', 1, 'x'.charCodeAt(0)])); lineData.setCell(4, CellData.fromCharData([DEFAULT_ATTR, 'x', 1, 'x'.charCodeAt(0)])); - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, 2, 4); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, 2, 4); assert.equal(extractHtml(spans), 'aax x' ); @@ -445,7 +454,7 @@ describe('DomRendererRowFactory', () => { for (let i = 0; i < 10; ++i) { lineData.setCell(i, CellData.fromCharData([DEFAULT_ATTR, 'a', 1, 'a'.charCodeAt(0)])); } - const spans = rowFactory.createRow(lineData, 0, false, undefined, 0, false, 5, EMPTY_WIDTH, -100, 100); + const spans = rowFactory.createRow(lineData, 0, false, undefined, undefined, 0, false, 5, EMPTY_WIDTH, -100, 100); assert.equal(extractHtml(spans), 'aaaaaaaaaa' ); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 3f9765f0..4c643720 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -27,7 +27,11 @@ export const enum RowCss { CURSOR_BLINK_CLASS = 'xterm-cursor-blink', CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block', CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar', - CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline' + CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline', + CURSOR_INACTIVE_STYLE_OUTLINE_CLASS = 'xterm-cursor-inactive-outline', + CURSOR_INACTIVE_STYLE_LINE_CLASS = 'xterm-cursor-inactive-line', + CURSOR_INACTIVE_STYLE_UNDERLINE_CLASS = 'xterm-cursor-inactive-underline', + CURSOR_INACTIVE_STYLE_NONE_CLASS = 'xterm-cursor-inactive-none' } @@ -61,6 +65,7 @@ export class DomRendererRowFactory { row: number, isCursorRow: boolean, cursorStyle: string | undefined, + cursorInactiveStyle: string | undefined, cursorX: number, cursorBlink: boolean, cellWidth: number, @@ -207,6 +212,22 @@ export class DomRendererRowFactory { ? RowCss.CURSOR_STYLE_UNDERLINE_CLASS : RowCss.CURSOR_STYLE_BLOCK_CLASS ); + if (cursorInactiveStyle) { + switch (cursorInactiveStyle) { + case 'outline': + classes.push(RowCss.CURSOR_INACTIVE_STYLE_OUTLINE_CLASS); + break; + case 'line': + classes.push(RowCss.CURSOR_INACTIVE_STYLE_LINE_CLASS); + break; + case 'underline': + classes.push(RowCss.CURSOR_INACTIVE_STYLE_UNDERLINE_CLASS); + break; + default: + classes.push(RowCss.CURSOR_INACTIVE_STYLE_NONE_CLASS); + break; + } + } } if (cell.isBold()) { diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index 8b978673..c605e5fa 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -38,6 +38,8 @@ export interface ITerminalOptions extends IPublicTerminalOptions { export type CursorStyle = 'block' | 'underline' | 'bar'; +export type CursorInactiveStyle = 'outline' | 'line' | 'underline' | 'none'; + export type XtermListener = (...args: any[]) => void; /** diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index cbeb6188..fb4251d9 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -15,6 +15,7 @@ export const DEFAULT_OPTIONS: Readonly> = { cursorBlink: false, cursorStyle: 'block', cursorWidth: 1, + cursorInactiveStyle: 'outline', customGlyphs: true, drawBoldTextInBrightColors: true, fastScrollModifier: 'alt', diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 09b11083..f31b6be0 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -5,7 +5,7 @@ import { IEvent, IEventEmitter } from 'common/EventEmitter'; import { IBuffer, IBufferSet } from 'common/buffer/Types'; -import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource, IDisposable, IColor, CursorStyle, IOscLinkData } from 'common/Types'; +import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource, IDisposable, IColor, CursorStyle, CursorInactiveStyle, IOscLinkData } from 'common/Types'; import { createDecorator } from 'common/services/ServiceRegistry'; import { IDecorationOptions, IDecoration, ILinkHandler, IWindowsPty, ILogger } from 'xterm'; @@ -212,6 +212,7 @@ export interface ITerminalOptions { cursorBlink?: boolean; cursorStyle?: CursorStyle; cursorWidth?: number; + cursorInactiveStyle?: CursorInactiveStyle; customGlyphs?: boolean; disableStdin?: boolean; drawBoldTextInBrightColors?: boolean; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 954970dc..78c3bf71 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -69,6 +69,11 @@ declare module 'xterm' { */ cursorWidth?: number; + /** + * The style of the inactive cursor. + */ + cursorInactiveStyle?: 'outline' | 'line' | 'underline' | 'none'; + /** * Whether to draw custom glyphs for block element and box drawing characters instead of using * the font. This should typically result in better rendering with continuous lines, even when From 0321bcb715578c76eb731a4a92a9a53630672a36 Mon Sep 17 00:00:00 2001 From: tisilent Date: Fri, 11 Aug 2023 09:40:06 +0800 Subject: [PATCH 2/6] Remove special classes. Add xterm-cursor-outline class. --- src/browser/renderer/dom/DomRenderer.ts | 6 +- .../renderer/dom/DomRendererRowFactory.ts | 57 +++++++++---------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index be703ebe..5a83427a 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -197,14 +197,14 @@ export class DomRenderer extends Disposable implements IRenderer { `}`; // Cursor styles += - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_INACTIVE_STYLE_OUTLINE_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` + ` outline: 1px solid ${colors.cursor.css};` + ` outline-offset: -1px;` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_INACTIVE_STYLE_LINE_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` + ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_INACTIVE_STYLE_UNDERLINE_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` + ` border-bottom: 1px ${colors.cursor.css};` + ` border-bottom-style: solid;` + ` height: calc(100% - 1px);` + diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 4c643720..4471b34e 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -26,12 +26,9 @@ export const enum RowCss { CURSOR_CLASS = 'xterm-cursor', CURSOR_BLINK_CLASS = 'xterm-cursor-blink', CURSOR_STYLE_BLOCK_CLASS = 'xterm-cursor-block', + CURSOR_STYLE_OUTLINE_CLASS = 'xterm-cursor-outline', CURSOR_STYLE_BAR_CLASS = 'xterm-cursor-bar', - CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline', - CURSOR_INACTIVE_STYLE_OUTLINE_CLASS = 'xterm-cursor-inactive-outline', - CURSOR_INACTIVE_STYLE_LINE_CLASS = 'xterm-cursor-inactive-line', - CURSOR_INACTIVE_STYLE_UNDERLINE_CLASS = 'xterm-cursor-inactive-underline', - CURSOR_INACTIVE_STYLE_NONE_CLASS = 'xterm-cursor-inactive-none' + CURSOR_STYLE_UNDERLINE_CLASS = 'xterm-cursor-underline' } @@ -202,30 +199,32 @@ export class DomRendererRowFactory { if (!this._coreService.isCursorHidden && isCursorCell) { classes.push(RowCss.CURSOR_CLASS); - if (cursorBlink) { - classes.push(RowCss.CURSOR_BLINK_CLASS); - } - classes.push( - cursorStyle === 'bar' - ? RowCss.CURSOR_STYLE_BAR_CLASS - : cursorStyle === 'underline' - ? RowCss.CURSOR_STYLE_UNDERLINE_CLASS - : RowCss.CURSOR_STYLE_BLOCK_CLASS - ); - if (cursorInactiveStyle) { - switch (cursorInactiveStyle) { - case 'outline': - classes.push(RowCss.CURSOR_INACTIVE_STYLE_OUTLINE_CLASS); - break; - case 'line': - classes.push(RowCss.CURSOR_INACTIVE_STYLE_LINE_CLASS); - break; - case 'underline': - classes.push(RowCss.CURSOR_INACTIVE_STYLE_UNDERLINE_CLASS); - break; - default: - classes.push(RowCss.CURSOR_INACTIVE_STYLE_NONE_CLASS); - break; + if (this._coreBrowserService.isFocused) { + if (cursorBlink) { + classes.push(RowCss.CURSOR_BLINK_CLASS); + } + classes.push( + cursorStyle === 'bar' + ? RowCss.CURSOR_STYLE_BAR_CLASS + : cursorStyle === 'underline' + ? RowCss.CURSOR_STYLE_UNDERLINE_CLASS + : RowCss.CURSOR_STYLE_BLOCK_CLASS + ); + } else { + if (cursorInactiveStyle) { + switch (cursorInactiveStyle) { + case 'outline': + classes.push(RowCss.CURSOR_STYLE_OUTLINE_CLASS); + break; + case 'line': + classes.push(RowCss.CURSOR_STYLE_BAR_CLASS); + break; + case 'underline': + classes.push(RowCss.CURSOR_STYLE_UNDERLINE_CLASS); + break; + default: + break; + } } } } From 5c1e6dce998e1b153bc3db32a230cefa0714b0cf Mon Sep 17 00:00:00 2001 From: tisilent Date: Fri, 11 Aug 2023 11:36:40 +0800 Subject: [PATCH 3/6] Add block to cursorInactiveStyle, change line to bar --- .../src/CursorRenderLayer.ts | 15 ++++++-------- addons/xterm-addon-webgl/src/WebglRenderer.ts | 20 ++++++++++++------- demo/client.ts | 2 +- src/browser/renderer/dom/DomRenderer.ts | 18 +++++------------ .../renderer/dom/DomRendererRowFactory.ts | 4 +++- src/common/Types.d.ts | 2 +- typings/xterm.d.ts | 2 +- 7 files changed, 30 insertions(+), 33 deletions(-) diff --git a/addons/xterm-addon-canvas/src/CursorRenderLayer.ts b/addons/xterm-addon-canvas/src/CursorRenderLayer.ts index b35cdbbc..2ef1d072 100644 --- a/addons/xterm-addon-canvas/src/CursorRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/CursorRenderLayer.ts @@ -58,7 +58,8 @@ export class CursorRenderLayer extends BaseRenderLayer { this._cursorRenderers = { 'bar': this._renderBarCursor.bind(this), 'block': this._renderBlockCursor.bind(this), - 'underline': this._renderUnderlineCursor.bind(this) + 'underline': this._renderUnderlineCursor.bind(this), + 'outline': this._renderOutlineCursor.bind(this) }; this.register(optionsService.onOptionChange(() => this._handleOptionsChanged())); this._handleOptionsChanged(); @@ -150,13 +151,9 @@ export class CursorRenderLayer extends BaseRenderLayer { this._ctx.save(); this._ctx.fillStyle = this._themeService.colors.cursor.css; const cursorStyle = this._optionsService.rawOptions.cursorStyle; - if (this._optionsService.rawOptions.cursorInactiveStyle === 'outline') { - this._renderBlurCursor(cursorX, viewportRelativeCursorY, this._cell); - } else if (this._optionsService.rawOptions.cursorInactiveStyle === 'line') { - this._cursorRenderers['bar'](cursorX, viewportRelativeCursorY, this._cell); - } else if (this._optionsService.rawOptions.cursorInactiveStyle === 'underline') { - this._cursorRenderers['underline'](cursorX, viewportRelativeCursorY, this._cell); - } else { + const cursorInactiveStyle = this._optionsService.rawOptions.cursorInactiveStyle; + if (cursorInactiveStyle && cursorInactiveStyle !== 'none') { + this._cursorRenderers[cursorInactiveStyle](cursorX, viewportRelativeCursorY, this._cell); } this._ctx.restore(); this._state.x = cursorX; @@ -238,7 +235,7 @@ export class CursorRenderLayer extends BaseRenderLayer { this._ctx.restore(); } - private _renderBlurCursor(x: number, y: number, cell: ICellData): void { + private _renderOutlineCursor(x: number, y: number, cell: ICellData): void { this._ctx.save(); this._ctx.strokeStyle = this._themeService.colors.cursor.css; this._strokeRectAtCell(x, y, cell.getWidth(), 1); diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index f7d190ee..9bd30cb5 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -455,21 +455,24 @@ export class WebglRenderer extends Disposable implements IRenderer { // Override colors for cursor cell if (isCursorVisible && row === cursorY) { + const inactiveCursorStyle = this._getInactiveCursorStyle(terminal.options.cursorInactiveStyle); if (x === cursorX) { this._model.cursor = { x: cursorX, y: this._terminal.buffer.active.cursorY, width: cell.getWidth(), style: this._coreBrowserService.isFocused ? - (terminal.options.cursorStyle || 'block') : this._getInactiveCursorStyle(terminal.options.cursorInactiveStyle), + (terminal.options.cursorStyle || 'block') : inactiveCursorStyle, cursorWidth: terminal.options.cursorWidth, dpr: this._devicePixelRatio }; lastCursorX = cursorX + cell.getWidth() - 1; } if (x >= cursorX && x <= lastCursorX && - this._coreBrowserService.isFocused && - (terminal.options.cursorStyle || 'block') === 'block') { + ((this._coreBrowserService.isFocused && + (terminal.options.cursorStyle || 'block') === 'block') || + (this._coreBrowserService.isFocused === false && + inactiveCursorStyle === 'block'))) { this._cellColorResolver.result.fg = Attributes.CM_RGB | (this._themeService.colors.cursorAccent.rgba >> 8 & Attributes.RGB_MASK); this._cellColorResolver.result.bg = @@ -601,17 +604,20 @@ export class WebglRenderer extends Disposable implements IRenderer { this._onRequestRedraw.fire({ start: cursorY, end: cursorY }); } - private _getInactiveCursorStyle(cursorInactiveStyle: 'outline' | 'line' | 'underline' | 'none'): string { + private _getInactiveCursorStyle(cursorInactiveStyle: 'outline' | 'block' | 'bar' | 'underline' | 'none'): string { if (cursorInactiveStyle === 'outline') { return 'blur'; } - if (cursorInactiveStyle === 'line') { + if (cursorInactiveStyle === 'block') { + return 'block'; + } + if (cursorInactiveStyle === 'bar') { return 'bar'; } - if (cursorInactiveStyle === 'underline'){ + if (cursorInactiveStyle === 'underline') { return 'underline'; } - return 'block'; + return ''; } } diff --git a/demo/client.ts b/demo/client.ts index b5c67208..c9111fec 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -419,7 +419,7 @@ function initOptions(term: TerminalType): void { ]; const stringOptions = { cursorStyle: ['block', 'underline', 'bar'], - cursorInactiveStyle: ['outline', 'line', 'underline', 'none'], + cursorInactiveStyle: ['outline', 'block', 'bar', 'underline', 'none'], fastScrollModifier: ['none', 'alt', 'ctrl', 'shift'], fontFamily: null, fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'], diff --git a/src/browser/renderer/dom/DomRenderer.ts b/src/browser/renderer/dom/DomRenderer.ts index 5a83427a..e607ae3e 100644 --- a/src/browser/renderer/dom/DomRenderer.ts +++ b/src/browser/renderer/dom/DomRenderer.ts @@ -197,28 +197,20 @@ export class DomRenderer extends Disposable implements IRenderer { `}`; // Cursor styles += - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` + - ` outline: 1px solid ${colors.cursor.css};` + - ` outline-offset: -1px;` + - `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` + - ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` + - `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_UNDERLINE_CLASS} {` + - ` border-bottom: 1px ${colors.cursor.css};` + - ` border-bottom-style: solid;` + - ` height: calc(100% - 1px);` + - `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}:not(.${RowCss.CURSOR_STYLE_BLOCK_CLASS}) {` + ` animation: blink_box_shadow` + `_` + this._terminalClass + ` 1s step-end infinite;` + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_BLINK_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + ` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` + `}` + - `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` + ` background-color: ${colors.cursor.css};` + ` color: ${colors.cursorAccent.css};` + `}` + + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` + + ` outline: 1px solid ${colors.cursor.css};` + + ` outline-offset: -1px;` + + `}` + `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BAR_CLASS} {` + ` box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${colors.cursor.css} inset;` + `}` + diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 4471b34e..89468dd4 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -216,7 +216,9 @@ export class DomRendererRowFactory { case 'outline': classes.push(RowCss.CURSOR_STYLE_OUTLINE_CLASS); break; - case 'line': + case 'block': + classes.push(RowCss.CURSOR_STYLE_BLOCK_CLASS); + case 'bar': classes.push(RowCss.CURSOR_STYLE_BAR_CLASS); break; case 'underline': diff --git a/src/common/Types.d.ts b/src/common/Types.d.ts index c605e5fa..fceb4e8e 100644 --- a/src/common/Types.d.ts +++ b/src/common/Types.d.ts @@ -38,7 +38,7 @@ export interface ITerminalOptions extends IPublicTerminalOptions { export type CursorStyle = 'block' | 'underline' | 'bar'; -export type CursorInactiveStyle = 'outline' | 'line' | 'underline' | 'none'; +export type CursorInactiveStyle = 'outline' | 'block' | 'bar' | 'underline' | 'none'; export type XtermListener = (...args: any[]) => void; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 78c3bf71..23aab9b5 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -72,7 +72,7 @@ declare module 'xterm' { /** * The style of the inactive cursor. */ - cursorInactiveStyle?: 'outline' | 'line' | 'underline' | 'none'; + cursorInactiveStyle?: 'outline' | 'block' | 'bar' | 'underline' | 'none'; /** * Whether to draw custom glyphs for block element and box drawing characters instead of using From c3d8c2a919bef1c21f20c77f5196c87cc2422813 Mon Sep 17 00:00:00 2001 From: tisilent Date: Fri, 11 Aug 2023 15:47:50 +0800 Subject: [PATCH 4/6] Update test and fix --- .../dom/DomRendererRowFactory.test.ts | 23 +++++++++++++++---- .../renderer/dom/DomRendererRowFactory.ts | 1 + 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/browser/renderer/dom/DomRendererRowFactory.test.ts b/src/browser/renderer/dom/DomRendererRowFactory.test.ts index e95952f2..609a5b4a 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.test.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.test.ts @@ -73,11 +73,26 @@ describe('DomRendererRowFactory', () => { }); it('should add class for inactive cursor', () => { - for (const inactiveStyle of ['outline', 'line', 'underline', 'none']){ + const coreBrowserService = new MockCoreBrowserService(); + coreBrowserService.isFocused = false; + const rowFactory = new DomRendererRowFactory( + dom.window.document, + new MockCharacterJoinerService(), + new MockOptionsService({ drawBoldTextInBrightColors: true }), + coreBrowserService, + new MockCoreService(), + new MockDecorationService(), + new MockThemeService() + ); + for (const inactiveStyle of ['outline', 'block', 'bar', 'underline', 'none']){ const spans = rowFactory.createRow(lineData, 0, true, 'block', inactiveStyle, 0, false, 5, EMPTY_WIDTH, -1, -1); - assert.equal(extractHtml(spans), - ` ` - ); + if (inactiveStyle === 'none') { + assert.equal(extractHtml(spans), + ` `); + } else { + assert.equal(extractHtml(spans), + ` `); + } } }); diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts index 89468dd4..41e66b96 100644 --- a/src/browser/renderer/dom/DomRendererRowFactory.ts +++ b/src/browser/renderer/dom/DomRendererRowFactory.ts @@ -218,6 +218,7 @@ export class DomRendererRowFactory { break; case 'block': classes.push(RowCss.CURSOR_STYLE_BLOCK_CLASS); + break; case 'bar': classes.push(RowCss.CURSOR_STYLE_BAR_CLASS); break; From 136e2f2afebace27f56435dae368577dce9a20f9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:07:04 -0700 Subject: [PATCH 5/6] Improve API wording --- typings/xterm.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 23aab9b5..345c100b 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -60,7 +60,7 @@ declare module 'xterm' { cursorBlink?: boolean; /** - * The style of the cursor. + * The style of the cursor when the terminal is focused. */ cursorStyle?: 'block' | 'underline' | 'bar'; @@ -70,7 +70,7 @@ declare module 'xterm' { cursorWidth?: number; /** - * The style of the inactive cursor. + * The style of the cursor when the terminal is not focused. */ cursorInactiveStyle?: 'outline' | 'block' | 'bar' | 'underline' | 'none'; From 6f6cc6a686dc91284212ee28820072c7ab2422ab Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:09:31 -0700 Subject: [PATCH 6/6] Remove string mapping, improve types, standardize on 'outline' --- .../src/RectangleRenderer.ts | 6 +++--- addons/xterm-addon-webgl/src/Types.d.ts | 4 +++- addons/xterm-addon-webgl/src/WebglRenderer.ts | 21 ++----------------- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 4706b442..b2e5d0fa 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -255,7 +255,7 @@ export class RectangleRenderer extends Disposable { let offset: number; let rectangleCount = 0; - if (cursor.style === 'bar' || cursor.style === 'blur') { + if (cursor.style === 'bar' || cursor.style === 'outline') { // Left edge offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._addRectangleFloat( @@ -268,7 +268,7 @@ export class RectangleRenderer extends Disposable { this._cursorFloat ); } - if (cursor.style === 'underline' || cursor.style === 'blur') { + if (cursor.style === 'underline' || cursor.style === 'outline') { // Bottom edge offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._addRectangleFloat( @@ -281,7 +281,7 @@ export class RectangleRenderer extends Disposable { this._cursorFloat ); } - if (cursor.style === 'blur') { + if (cursor.style === 'outline') { // Top edge offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._addRectangleFloat( diff --git a/addons/xterm-addon-webgl/src/Types.d.ts b/addons/xterm-addon-webgl/src/Types.d.ts index 73b15bc7..3eb3b300 100644 --- a/addons/xterm-addon-webgl/src/Types.d.ts +++ b/addons/xterm-addon-webgl/src/Types.d.ts @@ -16,11 +16,13 @@ export interface ICursorRenderModel { x: number; y: number; width: number; - style: string; + style: CursorStyle; cursorWidth: number; dpr: number; } +export type CursorStyle = 'outline' | 'block' | 'bar' | 'underline' | 'none'; + export interface IWebGL2RenderingContext extends WebGLRenderingContext { vertexAttribDivisor(index: number, divisor: number): void; createVertexArray(): IWebGLVertexArrayObject; diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 9bd30cb5..5e5e3aaa 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -455,14 +455,13 @@ export class WebglRenderer extends Disposable implements IRenderer { // Override colors for cursor cell if (isCursorVisible && row === cursorY) { - const inactiveCursorStyle = this._getInactiveCursorStyle(terminal.options.cursorInactiveStyle); if (x === cursorX) { this._model.cursor = { x: cursorX, y: this._terminal.buffer.active.cursorY, width: cell.getWidth(), style: this._coreBrowserService.isFocused ? - (terminal.options.cursorStyle || 'block') : inactiveCursorStyle, + (terminal.options.cursorStyle || 'block') : terminal.options.cursorInactiveStyle, cursorWidth: terminal.options.cursorWidth, dpr: this._devicePixelRatio }; @@ -472,7 +471,7 @@ export class WebglRenderer extends Disposable implements IRenderer { ((this._coreBrowserService.isFocused && (terminal.options.cursorStyle || 'block') === 'block') || (this._coreBrowserService.isFocused === false && - inactiveCursorStyle === 'block'))) { + terminal.options.cursorInactiveStyle === 'block'))) { this._cellColorResolver.result.fg = Attributes.CM_RGB | (this._themeService.colors.cursorAccent.rgba >> 8 & Attributes.RGB_MASK); this._cellColorResolver.result.bg = @@ -603,22 +602,6 @@ export class WebglRenderer extends Disposable implements IRenderer { const cursorY = this._terminal.buffer.active.cursorY; this._onRequestRedraw.fire({ start: cursorY, end: cursorY }); } - - private _getInactiveCursorStyle(cursorInactiveStyle: 'outline' | 'block' | 'bar' | 'underline' | 'none'): string { - if (cursorInactiveStyle === 'outline') { - return 'blur'; - } - if (cursorInactiveStyle === 'block') { - return 'block'; - } - if (cursorInactiveStyle === 'bar') { - return 'bar'; - } - if (cursorInactiveStyle === 'underline') { - return 'underline'; - } - return ''; - } } // TODO: Share impl with core