diff --git a/src/vs/base/browser/ui/inputbox/inputBox.css b/src/vs/base/browser/ui/inputbox/inputBox.css index 294ca475662f7d1bb23f2616417bfa0a9db58961..dc1240dfbc57a40b3eb38967b2979f350e5bc4e7 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.css +++ b/src/vs/base/browser/ui/inputbox/inputBox.css @@ -58,7 +58,12 @@ .monaco-inputbox > .wrapper > textarea.input { display: block; - overflow: hidden; + -ms-overflow-style: none; /* IE 10+ */ + overflow: -moz-scrollbars-none; /* Firefox */ +} + +.monaco-inputbox > .wrapper > textarea.input::-webkit-scrollbar { + display: none; } .monaco-inputbox > .wrapper > .mirror { @@ -116,4 +121,4 @@ background-repeat: no-repeat; width: 16px; height: 16px; -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index d92ae6e2dae998419a5aeb56a779b382a2506eee..59d0c9900b9e3b66b76236d818f6eb56affbe497 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -19,6 +19,9 @@ import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { HistoryNavigator } from 'vs/base/common/history'; import { IHistoryNavigationWidget } from 'vs/base/browser/history'; +import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { ScrollbarVisibility } from 'vs/base/common/scrollable'; +import { domEvent } from 'vs/base/browser/event'; const $ = dom.$; @@ -28,6 +31,7 @@ export interface IInputOptions extends IInputBoxStyles { readonly type?: string; readonly validationOptions?: IInputValidationOptions; readonly flexibleHeight?: boolean; + readonly flexibleMaxHeight?: number; readonly actions?: ReadonlyArray; } @@ -86,7 +90,6 @@ export class InputBox extends Widget { private contextViewProvider?: IContextViewProvider; element: HTMLElement; private input: HTMLInputElement; - private mirror: HTMLElement | undefined; private actionbar?: ActionBar; private options: IInputOptions; private message: IMessage | null; @@ -94,7 +97,12 @@ export class InputBox extends Widget { private ariaLabel: string; private validation?: IInputValidator; private state: 'idle' | 'open' | 'closed' = 'idle'; - private cachedHeight: number | null; + + private mirror: HTMLElement | undefined; + private cachedHeight: number | undefined; + private cachedContentHeight: number | undefined; + private maxHeight: number = Number.POSITIVE_INFINITY; + private scrollableElement: ScrollableElement | undefined; private inputBackground?: Color; private inputForeground?: Color; @@ -123,7 +131,6 @@ export class InputBox extends Widget { this.options = options || Object.create(null); mixin(this.options, defaultOpts, false); this.message = null; - this.cachedHeight = null; this.placeholder = this.options.placeholder || ''; this.ariaLabel = this.options.ariaLabel || ''; @@ -159,8 +166,26 @@ export class InputBox extends Widget { this.onblur(this.input, () => dom.removeClass(this.element, 'synthetic-focus')); if (this.options.flexibleHeight) { + this.maxHeight = typeof this.options.flexibleMaxHeight === 'number' ? this.options.flexibleMaxHeight : Number.POSITIVE_INFINITY; + this.mirror = dom.append(wrapper, $('div.mirror')); this.mirror.innerHTML = ' '; + + this.scrollableElement = new ScrollableElement(this.element, { vertical: ScrollbarVisibility.Auto }); + dom.append(container, this.scrollableElement.getDomNode()); + this._register(this.scrollableElement); + + // from ScrollableElement to DOM + this._register(this.scrollableElement.onScroll(e => this.input.scrollTop = e.scrollTop)); + + const onSelectionChange = Event.filter(domEvent(document, 'selectionchange'), () => { + const selection = document.getSelection(); + return !!selection && selection.anchorNode === wrapper; + }); + + // from DOM to ScrollableElement + this._register(onSelectionChange(this.updateScrollDimensions, this)); + this._register(this.onDidHeightChange(this.updateScrollDimensions, this)); } else { this.input.type = this.options.type || 'text'; this.input.setAttribute('wrap', 'off'); @@ -250,7 +275,7 @@ export class InputBox extends Widget { } public get height(): number { - return this.cachedHeight === null ? dom.getTotalHeight(this.element) : this.cachedHeight; + return typeof this.cachedHeight === 'number' ? this.cachedHeight : dom.getTotalHeight(this.element); } public focus(): void { @@ -301,6 +326,19 @@ export class InputBox extends Widget { } } + private updateScrollDimensions(): void { + if (typeof this.cachedContentHeight !== 'number' || typeof this.cachedHeight !== 'number') { + return; + } + + const scrollHeight = this.cachedContentHeight; + const height = this.cachedHeight; + const scrollTop = this.input.scrollTop; + + this.scrollableElement!.setScrollDimensions({ scrollHeight, height }); + this.scrollableElement!.setScrollPosition({ scrollTop }); + } + public showMessage(message: IMessage, force?: boolean): void { this.message = message; @@ -510,12 +548,13 @@ export class InputBox extends Widget { return; } - const previousHeight = this.cachedHeight; - this.cachedHeight = dom.getTotalHeight(this.mirror); + const previousHeight = this.cachedContentHeight; + this.cachedContentHeight = dom.getTotalHeight(this.mirror); - if (previousHeight !== this.cachedHeight) { + if (previousHeight !== this.cachedContentHeight) { + this.cachedHeight = Math.min(this.cachedContentHeight, this.maxHeight); this.input.style.height = this.cachedHeight + 'px'; - this._onDidHeightChange.fire(this.cachedHeight); + this._onDidHeightChange.fire(this.cachedContentHeight); } } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 00e6d9356ce33c5d7e0327211e86a8b808338971..5c596e9ef7df7cc09d229dc77964ae488452ce47 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -797,7 +797,7 @@ export class RepositoryPanel extends ViewletPanel { const triggerValidation = () => validationDelayer.trigger(validate); - this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { flexibleHeight: true }); + this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { flexibleHeight: true, flexibleMaxHeight: 134 }); this.inputBox.setEnabled(this.isBodyVisible()); this._register(attachInputBoxStyler(this.inputBox, this.themeService)); this._register(this.inputBox); @@ -896,11 +896,8 @@ export class RepositoryPanel extends ViewletPanel { const listHeight = height - (editorHeight + 12 /* margin */); this.listContainer.style.height = `${listHeight}px`; this.list.layout(listHeight, width); - - toggleClass(this.inputBoxContainer, 'scroll', editorHeight >= 134); } else { addClass(this.inputBoxContainer, 'hidden'); - removeClass(this.inputBoxContainer, 'scroll'); this.listContainer.style.height = `${height}px`; this.list.layout(height, width);