提交 30018013 编写于 作者: A Alex Ross

Work in progress

上级 e146527f
......@@ -610,7 +610,7 @@ export class MouseController<T> implements IDisposable {
this.list.setFocus([focus], e.browserEvent);
if (!isMouseRightClick(e.browserEvent)) {
if (!isMouseRightClick(e.browserEvent) && !e.browserEvent.ctrlKey) {
this.list.setSelection([focus], e.browserEvent);
if (this.openController.shouldOpen(e.browserEvent)) {
......
......@@ -1105,6 +1105,21 @@ export class QuickInputService extends Component implements IQuickInputService {
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant() || '');
}
}));
this._register(list.onCtrlClickElement(() => {
if (this.controller instanceof QuickPick) {
if (!this.controller.canSelectMany) {
this.controller.canSelectMany = true;
} else {
// Defer to avoid the list reacting to the selection change
setTimeout(() => {
console.log('SETTING!!!!!');
if (this.controller instanceof QuickPick) {
this.controller.canSelectMany = false;
}
}, 0);
}
}
}));
const focusTracker = dom.trackFocus(container);
this._register(focusTracker);
......
......@@ -234,6 +234,8 @@ export class QuickInputList {
onButtonTriggered = this._onButtonTriggered.event;
private _onLeave = new Emitter<void>();
onLeave: Event<void> = this._onLeave.event;
private _onCtrlClickElement = new Emitter<void>();
onCtrlClickElement = this._onCtrlClickElement.event;
private _fireCheckedEvents = true;
private elementDisposables: IDisposable[] = [];
private disposables: IDisposable[] = [];
......@@ -287,6 +289,10 @@ export class QuickInputList {
// Works around / fixes #64350.
e.browserEvent.preventDefault();
}
if (e.browserEvent.ctrlKey) {
this.list.setSelection([]);
this._onCtrlClickElement.fire();
}
}));
this.disposables.push(dom.addDisposableListener(this.container, dom.EventType.CLICK, e => {
if (e.x || e.y) { // Avoid 'click' triggered by 'space' on checkbox.
......
......@@ -103,12 +103,21 @@ export class FileDialogService implements IFileDialogService {
if (this.shouldUseSimplified(schema)) {
const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder');
const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => {
if (uri) {
return (this.fileService.resolve(uri)).then(stat => {
const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uris => {
if (uris) {
const promises: Promise<IURIToOpen>[] = [];
uris.forEach(uri => {
promises.push(this.fileService.resolve(uri).then(stat => {
return stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
}));
});
return Promise.all(promises).then(files => {
return this.windowService.openWindow(files, { forceNewWindow: options.forceNewWindow });
});
// return (this.fileService.resolve(uri)).then(stat => {
// const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
// return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
// });
}
return undefined;
});
......@@ -127,9 +136,12 @@ export class FileDialogService implements IFileDialogService {
if (this.shouldUseSimplified(schema)) {
const title = nls.localize('openFile.title', 'Open File');
const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => {
if (uri) {
return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uris => {
if (uris) {
const files: IURIToOpen[] = uris.map(uri => {
return { fileUri: uri };
});
return this.windowService.openWindow(files, { forceNewWindow: options.forceNewWindow });
}
return undefined;
});
......@@ -148,9 +160,12 @@ export class FileDialogService implements IFileDialogService {
if (this.shouldUseSimplified(schema)) {
const title = nls.localize('openFolder.title', 'Open Folder');
const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
return this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uri => {
if (uri) {
return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }).then(uris => {
if (uris) {
const folders: IURIToOpen[] = uris.map(uri => {
return { folderUri: uri };
});
return this.windowService.openWindow(folders, { forceNewWindow: options.forceNewWindow });
}
return undefined;
});
......@@ -170,9 +185,12 @@ export class FileDialogService implements IFileDialogService {
const title = nls.localize('openWorkspace.title', 'Open Workspace');
const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }];
const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }).then(uri => {
if (uri) {
return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
return this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }).then(uris => {
if (uris) {
const workspaces: IURIToOpen[] = uris.map(uri => {
return { workspaceUri: uri };
});
return this.windowService.openWindow(workspaces, { forceNewWindow: options.forceNewWindow });
}
return undefined;
});
......@@ -215,7 +233,7 @@ export class FileDialogService implements IFileDialogService {
options.availableFileSystems = [schema]; // by default only allow loading in the own file system
}
return this.pickRemoteResource(options).then(uri => {
return uri ? [uri] : undefined;
return uri;
});
}
......@@ -246,7 +264,7 @@ export class FileDialogService implements IFileDialogService {
return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined);
}
private pickRemoteResource(options: IOpenDialogOptions): Promise<URI | undefined> {
private pickRemoteResource(options: IOpenDialogOptions): Promise<URI[] | undefined> {
const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog);
return remoteFileDialog.showOpenDialog(options);
}
......
......@@ -78,7 +78,7 @@ export class RemoteFileDialog {
this.contextKey = RemoteFileDialogContext.bindTo(contextKeyService);
}
public async showOpenDialog(options: IOpenDialogOptions = {}): Promise<URI | undefined> {
public async showOpenDialog(options: IOpenDialogOptions = {}): Promise<URI[] | undefined> {
this.scheme = this.getScheme(options.defaultUri, options.availableFileSystems);
this.userHome = await this.getUserHome();
const newOptions = await this.getOptions(options);
......@@ -103,7 +103,7 @@ export class RemoteFileDialog {
return new Promise<URI | undefined>((resolve) => {
this.pickResource(true).then(folderUri => {
resolve(folderUri);
resolve(folderUri ? folderUri[0] : undefined);
});
});
}
......@@ -145,7 +145,7 @@ export class RemoteFileDialog {
return URI.from({ scheme: this.scheme, path: this.environmentService.userHome });
}
private async pickResource(isSave: boolean = false): Promise<URI | undefined> {
private async pickResource(isSave: boolean = false): Promise<URI[] | undefined> {
this.allowFolderSelection = !!this.options.canSelectFolders;
this.allowFileSelection = !!this.options.canSelectFiles;
this.hidden = false;
......@@ -175,7 +175,7 @@ export class RemoteFileDialog {
}
}
return new Promise<URI | undefined>(async (resolve) => {
return new Promise<URI[] | undefined>(async (resolve) => {
this.filePickBox = this.quickInputService.createQuickPick<FileQuickPickItem>();
this.filePickBox.matchOnLabel = false;
this.filePickBox.autoFocusOnList = false;
......@@ -198,13 +198,13 @@ export class RemoteFileDialog {
this.currentFolder = homedir;
this.userEnteredPathSegment = '';
this.autoCompletePathSegment = '';
// this.filePickBox.canSelectMany = true;
this.filePickBox.title = this.options.title;
this.filePickBox.value = this.pathFromUri(this.currentFolder, true);
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
this.filePickBox.items = [];
function doResolve(dialog: RemoteFileDialog, uri: URI | undefined) {
function doResolve(dialog: RemoteFileDialog, uri: URI[] | undefined) {
resolve(uri);
dialog.contextKey.set(false);
dialog.filePickBox.dispose();
......@@ -223,11 +223,11 @@ export class RemoteFileDialog {
this.options.defaultUri = undefined;
if (this.requiresTrailing) {
return this.fileDialogService.showSaveDialog(this.options).then(result => {
doResolve(this, result);
doResolve(this, result ? [result] : undefined);
});
} else {
return this.fileDialogService.showOpenDialog(this.options).then(result => {
doResolve(this, result ? result[0] : undefined);
doResolve(this, result);
});
}
});
......@@ -254,11 +254,18 @@ export class RemoteFileDialog {
this.filePickBox.onDidChangeActive(i => {
isAcceptHandled = false;
// update input box to match the first selected item
if ((i.length === 1) && this.isChangeFromUser()) {
if (this.isChangeFromUser()) {
this.filePickBox.validationMessage = undefined;
this.setAutoComplete(this.constructFullUserPath(), this.userEnteredPathSegment, i[0], true);
if (i.length === 1) {
this.setAutoComplete(this.constructFullUserPath(), this.userEnteredPathSegment, i[0], true);
} else {
this.setAutoComplete(this.constructFullUserPath(), '', undefined);
}
}
});
this.filePickBox.onDidChangeSelection(i => {
this.setAutoComplete(this.constructFullUserPath(), '', undefined);
});
this.filePickBox.onDidChangeValue(async value => {
// onDidChangeValue can also be triggered by the auto complete, so if it looks like the auto complete, don't do anything
......@@ -314,8 +321,8 @@ export class RemoteFileDialog {
return this.pathAppend(this.currentFolder, this.userEnteredPathSegment);
}
private async onDidAccept(): Promise<URI | undefined> {
let resolveValue: URI | undefined;
private async onDidAccept(): Promise<URI[] | undefined> {
let resolveValue: URI[] | undefined;
let navigateValue: URI | undefined;
const trimmedPickBoxValue = ((this.filePickBox.value.length > 1) && this.endsWithSlash(this.filePickBox.value)) ? this.filePickBox.value.substr(0, this.filePickBox.value.length - 1) : this.filePickBox.value;
const inputUri = this.remoteUriFrom(trimmedPickBoxValue);
......@@ -330,11 +337,16 @@ export class RemoteFileDialog {
}
// Find resolve value
if (this.filePickBox.activeItems.length === 0) {
if (this.filePickBox.selectedItems && this.filePickBox.selectedItems.length > 0) {
resolveValue = [];
this.filePickBox.selectedItems.forEach(selectedItem => {
resolveValue!.push(selectedItem.uri);
});
} else if (this.filePickBox.activeItems.length === 0) {
if (!this.requiresTrailing && resources.isEqual(this.currentFolder, inputUri, true)) {
resolveValue = inputUri;
resolveValue = [inputUri];
} else if (statDirname && statDirname.isDirectory) {
resolveValue = inputUri;
resolveValue = [inputUri];
} else if (stat && stat.isDirectory) {
navigateValue = inputUri;
}
......@@ -342,7 +354,7 @@ export class RemoteFileDialog {
const item = this.filePickBox.selectedItems[0];
if (item) {
if (!item.isFolder) {
resolveValue = item.uri;
resolveValue = [item.uri];
} else {
navigateValue = item.uri;
}
......@@ -350,7 +362,9 @@ export class RemoteFileDialog {
}
if (resolveValue) {
resolveValue = this.addPostfix(resolveValue);
if (resolveValue.length === 1) {
resolveValue[0] = this.addPostfix(resolveValue[0]);
}
if (await this.validate(resolveValue)) {
return Promise.resolve(resolveValue);
}
......@@ -434,8 +448,14 @@ export class RemoteFileDialog {
}
}
private setAutoComplete(startingValue: string, startingBasename: string, quickPickItem: FileQuickPickItem, force: boolean = false): boolean {
if (this.filePickBox.busy) {
private setAutoComplete(startingValue: string, startingBasename: string, quickPickItem: FileQuickPickItem | undefined, force: boolean = false): boolean {
if (!quickPickItem) {
this.autoCompletePathSegment = '';
this.userEnteredPathSegment = '';
this.filePickBox.valueSelection = [startingValue.length, this.filePickBox.value.length];
this.insertText(startingValue, '');
return false;
} else if (this.filePickBox.busy) {
// We're in the middle of something else. Doing an auto complete now can result jumbled or incorrect autocompletes.
this.userEnteredPathSegment = startingBasename;
this.autoCompletePathSegment = '';
......@@ -536,49 +556,55 @@ export class RemoteFileDialog {
});
}
private async validate(uri: URI): Promise<boolean> {
let stat: IFileStat | undefined;
let statDirname: IFileStat | undefined;
try {
statDirname = await this.fileService.resolve(resources.dirname(uri));
stat = await this.fileService.resolve(uri);
} catch (e) {
// do nothing
}
if (this.requiresTrailing) { // save
if (stat && stat.isDirectory) {
// Can't do this
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateFolder', 'The folder already exists. Please use a new file name.');
return Promise.resolve(false);
} else if (stat && !this.shouldOverwriteFile) {
// Replacing a file.
this.shouldOverwriteFile = true;
// Show a yes/no prompt
const message = nls.localize('remoteFileDialog.validateExisting', '{0} already exists. Are you sure you want to overwrite it?', resources.basename(uri));
return this.yesNoPrompt(message);
} else if (!this.isValidBaseName(resources.basename(uri))) {
// Filename not allowed
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateBadFilename', 'Please enter a valid file name.');
return Promise.resolve(false);
} else if (!statDirname || !statDirname.isDirectory) {
// Folder to save in doesn't exist
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateNonexistentDir', 'Please enter a path that exists.');
return Promise.resolve(false);
private async validate(uri: URI[]): Promise<boolean> {
for (let i = 0; i < uri.length; i++) {
let stat: IFileStat | undefined;
let statDirname: IFileStat | undefined;
try {
statDirname = await this.fileService.resolve(resources.dirname(uri[i]));
stat = await this.fileService.resolve(uri[i]);
} catch (e) {
// do nothing
}
} else { // open
if (!stat) {
// File or folder doesn't exist
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateNonexistentDir', 'Please enter a path that exists.');
return Promise.resolve(false);
} else if (stat.isDirectory && !this.allowFolderSelection) {
// Folder selected when folder selection not permitted
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateFileOnly', 'Please select a file.');
return Promise.resolve(false);
} else if (!stat.isDirectory && !this.allowFileSelection) {
// File selected when file selection not permitted
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateFolderOnly', 'Please select a folder.');
return Promise.resolve(false);
if (this.requiresTrailing) { // save
if (uri.length > 1) {
// The uri array can only have one value in it. Something has gone very wrong otherwise.
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.tooMany', 'Can only Save As one item.');
return Promise.resolve(false);
} else if (stat && stat.isDirectory) {
// Can't do this
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateFolder', 'The folder already exists. Please use a new file name.');
return Promise.resolve(false);
} else if (stat && !this.shouldOverwriteFile) {
// Replacing a file.
this.shouldOverwriteFile = true;
// Show a yes/no prompt
const message = nls.localize('remoteFileDialog.validateExisting', '{0} already exists. Are you sure you want to overwrite it?', resources.basename(uri[i]));
return this.yesNoPrompt(message);
} else if (!this.isValidBaseName(resources.basename(uri[i]))) {
// Filename not allowed
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateBadFilename', 'Please enter a valid file name.');
return Promise.resolve(false);
} else if (!statDirname || !statDirname.isDirectory) {
// Folder to save in doesn't exist
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateNonexistentDir', 'Please enter a path that exists.');
return Promise.resolve(false);
}
} else { // open
if (!stat) {
// File or folder doesn't exist
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateNonexistentDir', 'Please enter a path that exists.');
return Promise.resolve(false);
} else if (stat.isDirectory && !this.allowFolderSelection) {
// Folder selected when folder selection not permitted
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateFileOnly', 'Please select a file.');
return Promise.resolve(false);
} else if (!stat.isDirectory && !this.allowFileSelection) {
// File selected when file selection not permitted
this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateFolderOnly', 'Please select a folder.');
return Promise.resolve(false);
}
}
}
return Promise.resolve(true);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册