diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 4cc73dadcfe53b2f6b43ecb13a3bdb51864599ab..68d1589ebca6fa3b70b4a9e52d3088fab9af99f8 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -295,6 +295,7 @@ export class FileDialogService implements IFileDialogService { showSaveDialog(options: ISaveDialogOptions): Promise { const schema = this.getFileSystemSchema(options); if (schema !== Schemas.file) { + options.availableFileSystems = [schema, Schemas.file]; // always allow file as well return this.saveRemoteResource(options); } diff --git a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts index ea617b1c68ac6c5de2cae9717c2d096a5cb9dc4f..51c7a6b149e8141cd73e5a440c6f3c8e5a612a64 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts @@ -10,7 +10,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IQuickInputService, IQuickPickItem, IQuickPick } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; import { isWindows } from 'vs/base/common/platform'; -import { ISaveDialogOptions, IOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; +import { ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IWindowService, IURIToOpen } from 'vs/platform/windows/common/windows'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -27,7 +27,7 @@ const INVALID_FILE_CHARS = isWindows ? /[\\/:\*\?"<>\|]/g : /[\\/]/g; const WINDOWS_FORBIDDEN_NAMES = /^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i; export class RemoteFileDialog { - + private fallbackPickerButton = { iconPath: this.getAlternateDialogIcons(), tooltip: 'Use Alternate File System' }; private acceptButton = { iconPath: this.getIcons('accept.svg'), tooltip: 'Select' }; private cancelButton = { iconPath: this.getIcons('cancel.svg'), tooltip: 'Cancel' }; private currentFolder: URI; @@ -43,6 +43,7 @@ export class RemoteFileDialog { @ILabelService private readonly labelService: ILabelService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @INotificationService private readonly notificationService: INotificationService, + @IFileDialogService private readonly fileDialogService: IFileDialogService, ) { this.remoteAuthority = this.windowService.getConfiguration().remoteAuthority; } @@ -55,7 +56,7 @@ export class RemoteFileDialog { } const title = nls.localize('remoteFileDialog.openTitle', 'Open File or Folder'); - return this.pickResource({ title, defaultUri, canSelectFiles: true, canSelectFolders: true }).then(async fileFolderUri => { + return this.pickResource({ title, defaultUri, canSelectFiles: true, canSelectFolders: true, availableFileSystems: options.availableFileSystems }).then(async fileFolderUri => { if (fileFolderUri) { const stat = await this.remoteFileService.resolveFile(fileFolderUri); return [{ uri: fileFolderUri, typeHint: stat.isDirectory ? 'folder' : 'file' }]; @@ -82,11 +83,21 @@ export class RemoteFileDialog { saveNameBox.onDidChangeValue(v => { saveNameBox.validationMessage = this.isValidBaseName(v) ? void 0 : nls.localize('remoteFileDialog.error.invalidfilename', 'Not a valid file name'); }); + saveNameBox.buttons = [this.fallbackPickerButton]; + saveNameBox.onDidTriggerButton(button => { + if (button === this.fallbackPickerButton) { + options.availableFileSystems.shift(); + this.fileDialogService.showSaveDialog(options).then(result => { + resolve(result); + }); + } + saveNameBox.dispose(); + }); saveNameBox.onDidAccept(_ => { const name = saveNameBox.value; if (this.isValidBaseName(name)) { saveNameBox.hide(); - this.pickResource({ defaultUri: defaultUri, canSelectFolders: true, title: nls.localize('remoteFileDialogerror.titleFolderPage', 'Folder for \'{0}\'', name) }, { step: 2, totalSteps: 2 }).then(folderUri => { + this.pickResource({ defaultUri: defaultUri, canSelectFolders: true, title: nls.localize('remoteFileDialogerror.titleFolderPage', 'Folder for \'{0}\'', name), availableFileSystems: options.availableFileSystems }, { step: 2, totalSteps: 2 }).then(folderUri => { if (folderUri) { resolve(this.remoteUriFrom(this.remotePathJoin(folderUri, name))); } else { @@ -125,9 +136,19 @@ export class RemoteFileDialog { let isAcceptHandled = false; this.currentFolder = homedir; - this.filePickBox.buttons = [this.acceptButton, this.cancelButton]; + if (options.availableFileSystems.length > 1) { + this.filePickBox.buttons = [this.fallbackPickerButton, this.acceptButton, this.cancelButton]; + } else { + this.filePickBox.buttons = [this.acceptButton, this.cancelButton]; + } this.filePickBox.onDidTriggerButton(button => { - if (button === this.acceptButton) { + if (button === this.fallbackPickerButton) { + options.availableFileSystems.shift(); + isResolved = true; + this.fileDialogService.pickFileAndOpen(options).then(result => { + resolve(result ? result[0] : undefined); + }); + } else if (button === this.acceptButton) { resolve(this.currentFolder); isResolved = true; } @@ -311,4 +332,11 @@ export class RemoteFileDialog { dark: URI.parse(require.toUrl(`vs/workbench/services/dialogs/media/dark/${name}`)) }; } + + private getAlternateDialogIcons(): { light: URI, dark: URI } { + return { + dark: URI.parse(require.toUrl(`vs/editor/contrib/suggest/media/Folder_inverse_16x.svg`)), + light: URI.parse(require.toUrl(`vs/editor/contrib/suggest/media/Folder_16x.svg`)) + }; + } } \ No newline at end of file