diff --git a/css/xterm.css b/css/xterm.css index e9a1e3ad..b775f651 100644 --- a/css/xterm.css +++ b/css/xterm.css @@ -225,7 +225,19 @@ /* Arrows */ .xterm .xterm-scrollable-element > .scrollbar > .scra { cursor: pointer; - font-size: 11px !important; + background-color: var(--vscode-scrollbarSliderBackground, rgba(100, 100, 100, 0.4)); + mask-image: url("data:image/svg+xml;utf8,"); + -webkit-mask-image: url("data:image/svg+xml;utf8,"); + mask-repeat: no-repeat; + -webkit-mask-repeat: no-repeat; + mask-position: center center; + -webkit-mask-position: center center; + mask-size: 100% 100%; + -webkit-mask-size: 100% 100%; +} + +.xterm .xterm-scrollable-element > .scrollbar > .scra.xterm-arrow-down { + transform: rotate(180deg); } .xterm .xterm-scrollable-element > .visible { diff --git a/demo/client/client.ts b/demo/client/client.ts index 45bdd1d4..2665a398 100644 --- a/demo/client/client.ts +++ b/demo/client/client.ts @@ -275,6 +275,7 @@ function createTerminal(): Terminal { const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; term = new Terminal({ + scrollbar: { showArrows: true }, allowProposedApi: true, windowsPty: isWindows ? { // In a real scenario, these values should be verified on the backend diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts index 8110ff05..56b6fc30 100644 --- a/src/browser/Viewport.ts +++ b/src/browser/Viewport.ts @@ -56,6 +56,7 @@ export class Viewport extends Disposable { horizontal: ScrollbarVisibility.HIDDEN, useShadows: false, mouseWheelSmoothScroll: true, + verticalHasArrows: this._optionsService.rawOptions.scrollbar?.showArrows ?? false, ...this._getChangeOptions() }, scrollable)); this._register(this._optionsService.onMultipleOptionChange([ diff --git a/src/browser/scrollable/horizontalScrollbar.ts b/src/browser/scrollable/horizontalScrollbar.ts index 70058ca7..470410c4 100644 --- a/src/browser/scrollable/horizontalScrollbar.ts +++ b/src/browser/scrollable/horizontalScrollbar.ts @@ -17,7 +17,7 @@ export class HorizontalScrollbar extends AbstractScrollbar { lazyRender: options.lazyRender, host: host, scrollbarState: new ScrollbarState( - (options.horizontalHasArrows ? options.arrowSize : 0), + (options.horizontalHasArrows ? options.horizontalScrollbarSize : 0), (options.horizontal === ScrollbarVisibility.HIDDEN ? 0 : options.horizontalScrollbarSize), (options.vertical === ScrollbarVisibility.HIDDEN ? 0 : options.verticalScrollbarSize), scrollDimensions.width, diff --git a/src/browser/scrollable/scrollableElement.ts b/src/browser/scrollable/scrollableElement.ts index 7734d530..82ba75f1 100644 --- a/src/browser/scrollable/scrollableElement.ts +++ b/src/browser/scrollable/scrollableElement.ts @@ -572,7 +572,6 @@ function resolveOptions(opts: IScrollableElementCreationOptions): IScrollableEle fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5), scrollPredominantAxis: (typeof opts.scrollPredominantAxis !== 'undefined' ? opts.scrollPredominantAxis : true), mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true), - arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11), listenOnDomNode: (typeof opts.listenOnDomNode !== 'undefined' ? opts.listenOnDomNode : null), diff --git a/src/browser/scrollable/scrollableElementOptions.ts b/src/browser/scrollable/scrollableElementOptions.ts index 6787189e..8b241a60 100644 --- a/src/browser/scrollable/scrollableElementOptions.ts +++ b/src/browser/scrollable/scrollableElementOptions.ts @@ -67,11 +67,6 @@ export interface IScrollableElementCreationOptions { * Defaults to true. */ scrollPredominantAxis?: boolean; - /** - * Height for vertical arrows (top/bottom) and width for horizontal arrows (left/right). - * Defaults to 11. - */ - arrowSize?: number; /** * The dom node events should be bound to. * If no listenOnDomNode is provided, the constructor dom node is used. @@ -151,7 +146,6 @@ export interface IScrollableElementResolvedOptions { fastScrollSensitivity: number; scrollPredominantAxis: boolean; mouseWheelSmoothScroll: boolean; - arrowSize: number; listenOnDomNode: HTMLElement | null; horizontal: ScrollbarVisibility; horizontalScrollbarSize: number; diff --git a/src/browser/scrollable/scrollbarArrow.ts b/src/browser/scrollable/scrollbarArrow.ts index 5dc19da4..3b8f9adc 100644 --- a/src/browser/scrollable/scrollbarArrow.ts +++ b/src/browser/scrollable/scrollbarArrow.ts @@ -8,11 +8,6 @@ import { Widget } from './widget'; import { TimeoutTimer } from 'common/Async'; import * as dom from '../Dom'; -/** - * The arrow image size. - */ -const ARROW_IMG_SIZE = 11; - export interface IScrollbarArrowOptions { handleActivate: () => void; className: string; @@ -63,8 +58,9 @@ export class ScrollbarArrow extends Widget { // this.domNode.classList.add(...ThemeIcon.asClassNameArray(opts.icon)); this.domNode.style.position = 'absolute'; - this.domNode.style.width = ARROW_IMG_SIZE + 'px'; - this.domNode.style.height = ARROW_IMG_SIZE + 'px'; + const arrowSize = Math.min(opts.bgWidth, opts.bgHeight); + this.domNode.style.width = arrowSize + 'px'; + this.domNode.style.height = arrowSize + 'px'; if (typeof opts.top !== 'undefined') { this.domNode.style.top = opts.top + 'px'; } diff --git a/src/browser/scrollable/verticalScrollbar.ts b/src/browser/scrollable/verticalScrollbar.ts index e55373e2..5cecc87f 100644 --- a/src/browser/scrollable/verticalScrollbar.ts +++ b/src/browser/scrollable/verticalScrollbar.ts @@ -17,7 +17,7 @@ export class VerticalScrollbar extends AbstractScrollbar { lazyRender: options.lazyRender, host: host, scrollbarState: new ScrollbarState( - (options.verticalHasArrows ? options.arrowSize : 0), + (options.verticalHasArrows ? options.verticalScrollbarSize : 0), (options.vertical === ScrollbarVisibility.HIDDEN ? 0 : options.verticalScrollbarSize), 0, scrollDimensions.height, @@ -31,7 +31,24 @@ export class VerticalScrollbar extends AbstractScrollbar { }); if (options.verticalHasArrows) { - throw new Error('horizontalHasArrows is not supported in xterm.js'); + const arrowSize = options.verticalScrollbarSize; + const arrowDelta = 0; + this._createArrow({ + className: 'scra xterm-arrow-up', + top: arrowDelta, + left: arrowDelta, + bgWidth: options.verticalScrollbarSize, + bgHeight: arrowSize, + handleActivate: () => this._arrowScroll(-arrowSize) + }); + this._createArrow({ + className: 'scra xterm-arrow-down', + bottom: arrowDelta, + left: arrowDelta, + bgWidth: options.verticalScrollbarSize, + bgHeight: arrowSize, + handleActivate: () => this._arrowScroll(arrowSize) + }); } this._createSlider(0, Math.floor((options.verticalScrollbarSize - options.verticalSliderSize) / 2), options.verticalSliderSize, undefined); @@ -76,6 +93,11 @@ export class VerticalScrollbar extends AbstractScrollbar { target.scrollTop = scrollPosition; } + private _arrowScroll(delta: number): void { + const currentPosition = this._scrollable.getCurrentScrollPosition(); + this._scrollable.setScrollPositionNow({ scrollTop: currentPosition.scrollTop + delta }); + } + public updateOptions(options: IScrollableElementResolvedOptions): void { this.updateScrollbarSize(options.vertical === ScrollbarVisibility.HIDDEN ? 0 : options.verticalScrollbarSize); this._scrollbarState.setOppositeScrollbarSize(0); diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts index 2eab930f..3cb1b55b 100644 --- a/src/common/services/OptionsService.ts +++ b/src/common/services/OptionsService.ts @@ -31,6 +31,7 @@ export const DEFAULT_OPTIONS: Readonly> = { logLevel: 'info', logger: null, scrollback: 1000, + scrollbar: {}, scrollOnEraseInDisplay: false, scrollOnUserInput: true, scrollSensitivity: 1, diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts index 341c0bc2..4a88de3d 100644 --- a/src/common/services/Services.ts +++ b/src/common/services/Services.ts @@ -266,6 +266,7 @@ export interface ITerminalOptions { wordSeparator?: string; overviewRuler?: IOverviewRulerOptions; quirks?: ITerminalQuirks; + scrollbar?: IScrollbarOptions; scrollOnEraseInDisplay?: boolean; vtExtensions?: IVtExtensions; @@ -309,6 +310,10 @@ export interface ITerminalQuirks { allowSetCursorBlink?: boolean; } +export interface IScrollbarOptions { + showArrows?: boolean; +} + export interface IVtExtensions { kittyKeyboard?: boolean; kittySgrBoldFaintControl?: boolean; diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts index 19ab3da0..97901351 100644 --- a/typings/xterm.d.ts +++ b/typings/xterm.d.ts @@ -274,6 +274,11 @@ declare module '@xterm/xterm' { */ scrollSensitivity?: number; + /** + * Options for configuring the scrollbar. + */ + scrollbar?: IScrollbarOptions; + /** * The duration to smoothly scroll between the origin and the target in * milliseconds. Set to 0 to disable smooth scrolling and scroll instantly. @@ -722,6 +727,17 @@ declare module '@xterm/xterm' { showBottomBorder?: boolean; } + /** + * Options for configuring the scrollbar. + */ + export interface IScrollbarOptions { + /** + * Whether to show arrows at the top and bottom of the scrollbar. Defaults + * to false. + */ + showArrows?: boolean; + } + /** * Enable various window manipulation and report features * (`CSI Ps ; Ps ; Ps t`).