diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 52bbb1ec6662aba4e9b964b3dac85744ea9c23a3..f0a82a16741806a073e230767628450c8f2d63f1 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -570,6 +570,7 @@ export class Repository implements Disposable { const root = Uri.file(repository.root); this._sourceControl = scm.createSourceControl('git', 'Git', root); this._sourceControl.inputBox.placeholder = localize('commitMessage', "Message (press {0} to commit)"); + this._sourceControl.inputBox.visible = false; this._sourceControl.acceptInputCommand = { command: 'git.commitWithInput', title: localize('commit', "Commit"), arguments: [this._sourceControl] }; this._sourceControl.quickDiffProvider = this; this._sourceControl.inputBox.validateInput = this.validateInput.bind(this); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7b7b91e47552f80d3140c600021165a928c1c2e0..ab6599c8b40f392060e1f3b6d7e107c6ed446f45 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -720,11 +720,21 @@ declare module 'vscode' { * An event signaling when the selection state changes. */ readonly onDidChangeSelection: Event; + } + + //#endregion + + //#region Joao: SCM Input Box + + /** + * Represents the input box in the Source Control viewlet. + */ + export interface SourceControlInputBox { /** - * Whether the input box is hidden. + * Whether the input box is visible. */ - hideInputBox: boolean; + visible: boolean; } //#endregion diff --git a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts index 9a06495036e200431f667e8d241aebde953908eb..3602f7f3cb5a7470635b53822dce60201fa099f0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSCM.ts @@ -119,7 +119,6 @@ class MainThreadSCMProvider implements ISCMProvider { get acceptInputCommand(): Command | undefined { return this.features.acceptInputCommand; } get statusBarCommands(): Command[] | undefined { return this.features.statusBarCommands; } get count(): number | undefined { return this.features.count; } - get hideInputBox(): boolean | undefined { return this.features.hideInputBox; } private _onDidChangeCommitTemplate = new Emitter(); get onDidChangeCommitTemplate(): Event { return this._onDidChangeCommitTemplate.event; } @@ -396,6 +395,16 @@ export class MainThreadSCM implements MainThreadSCMShape { repository.input.placeholder = placeholder; } + $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void { + const repository = this._repositories[sourceControlHandle]; + + if (!repository) { + return; + } + + repository.input.visible = visible; + } + $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void { const repository = this._repositories[sourceControlHandle]; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 4c27fb518821562e481757cac95f6c4b80ad9191..80002e1cbe53b499476cd85d5518f990a29e0743 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -535,7 +535,6 @@ export interface SCMProviderFeatures { commitTemplate?: string; acceptInputCommand?: modes.Command; statusBarCommands?: modes.Command[]; - hideInputBox?: boolean; } export interface SCMGroupFeatures { @@ -580,6 +579,7 @@ export interface MainThreadSCMShape extends IDisposable { $setInputBoxValue(sourceControlHandle: number, value: string): void; $setInputBoxPlaceholder(sourceControlHandle: number, placeholder: string): void; + $setInputBoxVisibility(sourceControlHandle: number, visible: boolean): void; $setValidationProviderIsEnabled(sourceControlHandle: number, enabled: boolean): void; } diff --git a/src/vs/workbench/api/node/extHostSCM.ts b/src/vs/workbench/api/node/extHostSCM.ts index c98852392d374b2780bcce6fbb39e294e173b9ad..169dbf9650a5584076b933a94d6323370f06a6ea 100644 --- a/src/vs/workbench/api/node/extHostSCM.ts +++ b/src/vs/workbench/api/node/extHostSCM.ts @@ -198,6 +198,18 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { this._proxy.$setValidationProviderIsEnabled(this._sourceControlHandle, !!fn); } + private _visible: boolean = true; + + get visible(): boolean { + return this._visible; + } + + set visible(visible: boolean | undefined) { + visible = !!visible; + this._visible = visible; + this._proxy.$setInputBoxVisibility(this._sourceControlHandle, visible); + } + constructor(private _extension: IExtensionDescription, private _proxy: MainThreadSCMShape, private _sourceControlHandle: number) { // noop } @@ -439,17 +451,6 @@ class ExtHostSourceControl implements vscode.SourceControl { return this._selected; } - private _hideInputBox: boolean = false; - - get hideInputBox(): boolean { - return this._hideInputBox; - } - - set hideInputBox(hideInputBox: boolean | undefined) { - this._hideInputBox = hideInputBox; - this._proxy.$updateSourceControl(this.handle, { hideInputBox: !!hideInputBox }); - } - private _onDidChangeSelection = new Emitter(); readonly onDidChangeSelection = this._onDidChangeSelection.event; diff --git a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css index 0a15ee8d540a9b16a06cc952f28be972a20556b9..84ad51a4b7ba9faf76e125cc7834bd32b36275ac 100644 --- a/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css +++ b/src/vs/workbench/parts/scm/electron-browser/media/scmViewlet.css @@ -137,12 +137,12 @@ } .scm-viewlet .scm-editor { + box-sizing: border-box; padding: 5px 9px 5px 16px; } -.scm-viewlet .scm-editor { - box-sizing: border-box; - padding: 5px 9px 5px 16px; +.scm-viewlet .scm-editor.hidden { + display: none; } .scm-viewlet .scm-editor > .monaco-inputbox { diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 360940f0b3fcf47ddcda4b4f76e3f91e2d36c3a5..12be7344d788a9c6097f026157ed71ac8c55331e 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -11,7 +11,7 @@ import { domEvent, stop } from 'vs/base/browser/event'; import { basename } from 'vs/base/common/paths'; import { IDisposable, dispose, combinedDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { PanelViewlet, ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; -import { append, $, addClass, toggleClass, trackFocus, Dimension, addDisposableListener } from 'vs/base/browser/dom'; +import { append, $, addClass, toggleClass, trackFocus, Dimension, addDisposableListener, removeClass } from 'vs/base/browser/dom'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; @@ -814,59 +814,61 @@ export class RepositoryPanel extends ViewletPanel { this.disposables.push(focusTracker); // Input - if (!this.repository.provider.hideInputBox) { - this.inputBoxContainer = append(container, $('.scm-editor')); - - const updatePlaceholder = () => { - const binding = this.keybindingService.lookupKeybinding('scm.acceptInput'); - const label = binding ? binding.getLabel() : (platform.isMacintosh ? 'Cmd+Enter' : 'Ctrl+Enter'); - const placeholder = format(this.repository.input.placeholder, label); - - this.inputBox.setPlaceHolder(placeholder); - }; - - const validationDelayer = new ThrottledDelayer(200); - const validate = () => { - return this.repository.input.validateInput(this.inputBox.value, this.inputBox.inputElement.selectionStart).then(result => { - if (!result) { - this.inputBox.inputElement.removeAttribute('aria-invalid'); - this.inputBox.hideMessage(); - } else { - this.inputBox.inputElement.setAttribute('aria-invalid', 'true'); - this.inputBox.showMessage({ content: result.message, type: convertValidationType(result.type) }); - } - }); - }; - - const triggerValidation = () => validationDelayer.trigger(validate); - - this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { flexibleHeight: true }); - this.disposables.push(attachInputBoxStyler(this.inputBox, this.themeService)); - this.disposables.push(this.inputBox); - - this.inputBox.onDidChange(triggerValidation, null, this.disposables); - - const onKeyUp = domEvent(this.inputBox.inputElement, 'keyup'); - const onMouseUp = domEvent(this.inputBox.inputElement, 'mouseup'); - anyEvent(onKeyUp, onMouseUp)(triggerValidation, null, this.disposables); - - this.inputBox.value = this.repository.input.value; - this.inputBox.onDidChange(value => this.repository.input.value = value, null, this.disposables); - this.repository.input.onDidChange(value => this.inputBox.value = value, null, this.disposables); - - updatePlaceholder(); - this.repository.input.onDidChangePlaceholder(updatePlaceholder, null, this.disposables); - this.keybindingService.onDidUpdateKeybindings(updatePlaceholder, null, this.disposables); - - this.disposables.push(this.inputBox.onDidHeightChange(() => this.layoutBody())); - - if (this.repository.provider.onDidChangeCommitTemplate) { - this.repository.provider.onDidChangeCommitTemplate(this.updateInputBox, this, this.disposables); - } + this.inputBoxContainer = append(container, $('.scm-editor')); + + const updatePlaceholder = () => { + const binding = this.keybindingService.lookupKeybinding('scm.acceptInput'); + const label = binding ? binding.getLabel() : (platform.isMacintosh ? 'Cmd+Enter' : 'Ctrl+Enter'); + const placeholder = format(this.repository.input.placeholder, label); + + this.inputBox.setPlaceHolder(placeholder); + }; + + const validationDelayer = new ThrottledDelayer(200); + const validate = () => { + return this.repository.input.validateInput(this.inputBox.value, this.inputBox.inputElement.selectionStart).then(result => { + if (!result) { + this.inputBox.inputElement.removeAttribute('aria-invalid'); + this.inputBox.hideMessage(); + } else { + this.inputBox.inputElement.setAttribute('aria-invalid', 'true'); + this.inputBox.showMessage({ content: result.message, type: convertValidationType(result.type) }); + } + }); + }; + + const triggerValidation = () => validationDelayer.trigger(validate); + + this.inputBox = new InputBox(this.inputBoxContainer, this.contextViewService, { flexibleHeight: true }); + this.disposables.push(attachInputBoxStyler(this.inputBox, this.themeService)); + this.disposables.push(this.inputBox); + + this.inputBox.onDidChange(triggerValidation, null, this.disposables); + + const onKeyUp = domEvent(this.inputBox.inputElement, 'keyup'); + const onMouseUp = domEvent(this.inputBox.inputElement, 'mouseup'); + anyEvent(onKeyUp, onMouseUp)(triggerValidation, null, this.disposables); + + this.inputBox.value = this.repository.input.value; + this.inputBox.onDidChange(value => this.repository.input.value = value, null, this.disposables); + this.repository.input.onDidChange(value => this.inputBox.value = value, null, this.disposables); - this.updateInputBox(); + updatePlaceholder(); + this.repository.input.onDidChangePlaceholder(updatePlaceholder, null, this.disposables); + this.keybindingService.onDidUpdateKeybindings(updatePlaceholder, null, this.disposables); + + this.disposables.push(this.inputBox.onDidHeightChange(() => this.layoutBody())); + + if (this.repository.provider.onDidChangeCommitTemplate) { + this.repository.provider.onDidChangeCommitTemplate(this.updateInputBox, this, this.disposables); } + this.updateInputBox(); + + // Input box visibility + this.repository.input.onDidChangeVisibility(this.updateInputBoxVisibility, this, this.disposables); + this.updateInputBoxVisibility(); + // List this.listContainer = append(container, $('.scm-status.show-file-icons')); @@ -920,25 +922,35 @@ export class RepositoryPanel extends ViewletPanel { } this.cachedHeight = height; - if (this.inputBox) { + + if (this.repository.input.visible) { + removeClass(this.inputBoxContainer, 'hidden'); this.inputBox.layout(); - } - const editorHeight = this.inputBox ? this.inputBox.height : 0; - const listHeight = height - (editorHeight + (editorHeight ? 12 : 0) /* margin */); - this.listContainer.style.height = `${listHeight}px`; - this.list.layout(listHeight); + const editorHeight = this.inputBox.height; + const listHeight = height - (editorHeight + 12 /* margin */); + this.listContainer.style.height = `${listHeight}px`; + this.list.layout(listHeight); - if (this.inputBoxContainer) { 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); } } focus(): void { super.focus(); - if (this.isExpanded() && this.inputBox) { - this.inputBox.focus(); + if (this.isExpanded()) { + if (this.repository.input.visible) { + this.inputBox.focus(); + } else { + this.list.domFocus(); + } } } @@ -997,13 +1009,19 @@ export class RepositoryPanel extends ViewletPanel { } private updateInputBox(): void { - if (typeof this.repository.provider.commitTemplate === 'undefined' || !this.inputBox || this.inputBox.value) { + if (typeof this.repository.provider.commitTemplate === 'undefined' || !this.repository.input.visible || this.inputBox.value) { return; } this.inputBox.value = this.repository.provider.commitTemplate; } + private updateInputBoxVisibility(): void { + if (this.cachedHeight) { + this.layoutBody(this.cachedHeight); + } + } + dispose(): void { this.visibilityDisposables = dispose(this.visibilityDisposables); super.dispose(); diff --git a/src/vs/workbench/services/scm/common/scm.ts b/src/vs/workbench/services/scm/common/scm.ts index 54427b69fcb9403fe73cb139b88333a320187dd2..88882ad8cb1bb63fb147a4bf54f444ad18cfea96 100644 --- a/src/vs/workbench/services/scm/common/scm.ts +++ b/src/vs/workbench/services/scm/common/scm.ts @@ -63,8 +63,6 @@ export interface ISCMProvider extends IDisposable { readonly statusBarCommands?: Command[]; readonly onDidChange: Event; - readonly hideInputBox?: boolean | undefined; - getOriginalResource(uri: URI): TPromise; } @@ -92,6 +90,9 @@ export interface ISCMInput { validateInput: IInputValidator; readonly onDidChangeValidateInput: Event; + + visible: boolean; + readonly onDidChangeVisibility: Event; } export interface ISCMRepository extends IDisposable { diff --git a/src/vs/workbench/services/scm/common/scmService.ts b/src/vs/workbench/services/scm/common/scmService.ts index 1137b195abf6f24c3f324db922d68047754ddd2f..618d669a2b6089b561553e06c080487669d074d2 100644 --- a/src/vs/workbench/services/scm/common/scmService.ts +++ b/src/vs/workbench/services/scm/common/scmService.ts @@ -40,6 +40,20 @@ class SCMInput implements ISCMInput { private _onDidChangePlaceholder = new Emitter(); get onDidChangePlaceholder(): Event { return this._onDidChangePlaceholder.event; } + private _visible = true; + + get visible(): boolean { + return this._visible; + } + + set visible(visible: boolean) { + this._visible = visible; + this._onDidChangeVisibility.fire(visible); + } + + private _onDidChangeVisibility = new Emitter(); + get onDidChangeVisibility(): Event { return this._onDidChangeVisibility.event; } + private _validateInput: IInputValidator = () => TPromise.as(undefined); get validateInput(): IInputValidator {