@@ -16,6 +16,9 @@ import { IThemeService } from "vs/platform/theme/common/themeService";
1616import { workbench } from "./workbench" ;
1717import "./dialog.scss" ;
1818
19+ /**
20+ * Describes the type of dialog to show.
21+ */
1922export enum DialogType {
2023 NewFolder ,
2124 Save ,
@@ -68,8 +71,12 @@ interface DialogEntry {
6871 readonly isDirectory : boolean ;
6972 readonly size : number ;
7073 readonly lastModified : string ;
74+ readonly isDisabled ?: boolean ;
7175}
7276
77+ /**
78+ * Open and save dialogs.
79+ */
7380class Dialog {
7481 private _path : string | undefined ;
7582
@@ -265,8 +272,7 @@ class Dialog {
265272 }
266273 if ( element . isDirectory ) {
267274 this . path = element . fullPath ;
268- } else {
269- // Open
275+ } else if ( ! ( this . options as OpenDialogOptions ) . properties . openDirectory ) {
270276 this . selectEmitter . emit ( element . fullPath ) ;
271277 }
272278 } ) ;
@@ -282,12 +288,18 @@ class Dialog {
282288 } ) ;
283289 buttonsNode . appendChild ( cancelBtn ) ;
284290 const confirmBtn = document . createElement ( "button" ) ;
285- confirmBtn . innerText = "Confirm" ;
291+ const openFile = ( this . options as OpenDialogOptions ) . properties . openFile ;
292+ confirmBtn . innerText = openFile ? "Open" : "Confirm" ;
286293 confirmBtn . addEventListener ( "click" , ( ) => {
287- if ( this . _path ) {
294+ if ( this . _path && ! openFile ) {
288295 this . selectEmitter . emit ( this . _path ) ;
289296 }
290297 } ) ;
298+ // Since a single click opens a file, the only time this button can be
299+ // used is on a directory, which is invalid for opening files.
300+ if ( openFile ) {
301+ confirmBtn . disabled = true ;
302+ }
291303 buttonsNode . appendChild ( confirmBtn ) ;
292304 this . root . appendChild ( buttonsNode ) ;
293305 this . entryList . layout ( ) ;
@@ -303,13 +315,19 @@ class Dialog {
303315 return this . errorEmitter . event ;
304316 }
305317
318+ /**
319+ * Remove the dialog.
320+ */
306321 public dispose ( ) : void {
307322 this . selectEmitter . dispose ( ) ;
308323 this . errorEmitter . dispose ( ) ;
309324 this . entryList . dispose ( ) ;
310325 this . background . remove ( ) ;
311326 }
312327
328+ /**
329+ * Build and insert the path shown at the top of the dialog.
330+ */
313331 private buildPath ( ) : void {
314332 while ( this . pathNode . lastChild ) {
315333 this . pathNode . removeChild ( this . pathNode . lastChild ) ;
@@ -376,6 +394,9 @@ class Dialog {
376394 return ( < any > this . entryList ) . typeFilterController . filter . _pattern ;
377395 }
378396
397+ /**
398+ * List the files and return dialog entries.
399+ */
379400 private async list ( directory : string ) : Promise < ReadonlyArray < DialogEntry > > {
380401 const paths = ( await util . promisify ( fs . readdir ) ( directory ) ) . sort ( ) ;
381402 const stats = await Promise . all ( paths . map ( p => util . promisify ( fs . stat ) ( path . join ( directory , p ) ) ) ) ;
@@ -386,6 +407,8 @@ class Dialog {
386407 isDirectory : stat . isDirectory ( ) ,
387408 lastModified : stat . mtime . toDateString ( ) ,
388409 size : stat . size ,
410+ // If we are opening a directory, show files as disabled.
411+ isDisabled : ! stat . isDirectory ( ) && ( this . options as OpenDialogOptions ) . properties . openDirectory ,
389412 } ) ) ;
390413 }
391414}
@@ -397,11 +420,17 @@ interface DialogEntryData {
397420 label : HighlightedLabel ;
398421}
399422
423+ /**
424+ * Rendering for the different parts of a dialog entry.
425+ */
400426class DialogEntryRenderer implements ITreeRenderer < DialogEntry , string , DialogEntryData > {
401427 public get templateId ( ) : string {
402428 return "dialog-entry" ;
403429 }
404430
431+ /**
432+ * Append and return containers for each part of the dialog entry.
433+ */
405434 public renderTemplate ( container : HTMLElement ) : DialogEntryData {
406435 addClass ( container , "dialog-entry" ) ;
407436 addClass ( container , "dialog-grid" ) ;
@@ -422,6 +451,9 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
422451 } ;
423452 }
424453
454+ /**
455+ * Render a dialog entry.
456+ */
425457 public renderElement ( node : ITreeNode < DialogEntry , string > , _index : number , templateData : DialogEntryData ) : void {
426458 templateData . icon . className = "dialog-entry-icon monaco-icon-label" ;
427459 const classes = getIconClasses (
@@ -444,8 +476,19 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
444476 } ] : [ ] ) ;
445477 templateData . size . innerText = node . element . size . toString ( ) ;
446478 templateData . lastModified . innerText = node . element . lastModified ;
479+
480+ // We know this exists because we created the template.
481+ const entryContainer = templateData . label . element . parentElement ! . parentElement ! . parentElement ! ;
482+ if ( node . element . isDisabled ) {
483+ entryContainer . classList . add ( "disabled" ) ;
484+ } else {
485+ entryContainer . classList . remove ( "disabled" ) ;
486+ }
447487 }
448488
489+ /**
490+ * Does nothing (not implemented).
491+ */
449492 public disposeTemplate ( _templateData : DialogEntryData ) : void {
450493 // throw new Error("Method not implemented.");
451494 }
0 commit comments