@@ -16,6 +16,9 @@ import { IThemeService } from "vs/platform/theme/common/themeService";
16
16
import { workbench } from "./workbench" ;
17
17
import "./dialog.scss" ;
18
18
19
+ /**
20
+ * Describes the type of dialog to show.
21
+ */
19
22
export enum DialogType {
20
23
NewFolder ,
21
24
Save ,
@@ -68,8 +71,12 @@ interface DialogEntry {
68
71
readonly isDirectory : boolean ;
69
72
readonly size : number ;
70
73
readonly lastModified : string ;
74
+ readonly isDisabled ?: boolean ;
71
75
}
72
76
77
+ /**
78
+ * Open and save dialogs.
79
+ */
73
80
class Dialog {
74
81
private _path : string | undefined ;
75
82
@@ -265,8 +272,7 @@ class Dialog {
265
272
}
266
273
if ( element . isDirectory ) {
267
274
this . path = element . fullPath ;
268
- } else {
269
- // Open
275
+ } else if ( ! ( this . options as OpenDialogOptions ) . properties . openDirectory ) {
270
276
this . selectEmitter . emit ( element . fullPath ) ;
271
277
}
272
278
} ) ;
@@ -282,12 +288,18 @@ class Dialog {
282
288
} ) ;
283
289
buttonsNode . appendChild ( cancelBtn ) ;
284
290
const confirmBtn = document . createElement ( "button" ) ;
285
- confirmBtn . innerText = "Confirm" ;
291
+ const openFile = ( this . options as OpenDialogOptions ) . properties . openFile ;
292
+ confirmBtn . innerText = openFile ? "Open" : "Confirm" ;
286
293
confirmBtn . addEventListener ( "click" , ( ) => {
287
- if ( this . _path ) {
294
+ if ( this . _path && ! openFile ) {
288
295
this . selectEmitter . emit ( this . _path ) ;
289
296
}
290
297
} ) ;
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
+ }
291
303
buttonsNode . appendChild ( confirmBtn ) ;
292
304
this . root . appendChild ( buttonsNode ) ;
293
305
this . entryList . layout ( ) ;
@@ -303,13 +315,19 @@ class Dialog {
303
315
return this . errorEmitter . event ;
304
316
}
305
317
318
+ /**
319
+ * Remove the dialog.
320
+ */
306
321
public dispose ( ) : void {
307
322
this . selectEmitter . dispose ( ) ;
308
323
this . errorEmitter . dispose ( ) ;
309
324
this . entryList . dispose ( ) ;
310
325
this . background . remove ( ) ;
311
326
}
312
327
328
+ /**
329
+ * Build and insert the path shown at the top of the dialog.
330
+ */
313
331
private buildPath ( ) : void {
314
332
while ( this . pathNode . lastChild ) {
315
333
this . pathNode . removeChild ( this . pathNode . lastChild ) ;
@@ -376,6 +394,9 @@ class Dialog {
376
394
return ( < any > this . entryList ) . typeFilterController . filter . _pattern ;
377
395
}
378
396
397
+ /**
398
+ * List the files and return dialog entries.
399
+ */
379
400
private async list ( directory : string ) : Promise < ReadonlyArray < DialogEntry > > {
380
401
const paths = ( await util . promisify ( fs . readdir ) ( directory ) ) . sort ( ) ;
381
402
const stats = await Promise . all ( paths . map ( p => util . promisify ( fs . stat ) ( path . join ( directory , p ) ) ) ) ;
@@ -386,6 +407,8 @@ class Dialog {
386
407
isDirectory : stat . isDirectory ( ) ,
387
408
lastModified : stat . mtime . toDateString ( ) ,
388
409
size : stat . size ,
410
+ // If we are opening a directory, show files as disabled.
411
+ isDisabled : ! stat . isDirectory ( ) && ( this . options as OpenDialogOptions ) . properties . openDirectory ,
389
412
} ) ) ;
390
413
}
391
414
}
@@ -397,11 +420,17 @@ interface DialogEntryData {
397
420
label : HighlightedLabel ;
398
421
}
399
422
423
+ /**
424
+ * Rendering for the different parts of a dialog entry.
425
+ */
400
426
class DialogEntryRenderer implements ITreeRenderer < DialogEntry , string , DialogEntryData > {
401
427
public get templateId ( ) : string {
402
428
return "dialog-entry" ;
403
429
}
404
430
431
+ /**
432
+ * Append and return containers for each part of the dialog entry.
433
+ */
405
434
public renderTemplate ( container : HTMLElement ) : DialogEntryData {
406
435
addClass ( container , "dialog-entry" ) ;
407
436
addClass ( container , "dialog-grid" ) ;
@@ -422,6 +451,9 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
422
451
} ;
423
452
}
424
453
454
+ /**
455
+ * Render a dialog entry.
456
+ */
425
457
public renderElement ( node : ITreeNode < DialogEntry , string > , _index : number , templateData : DialogEntryData ) : void {
426
458
templateData . icon . className = "dialog-entry-icon monaco-icon-label" ;
427
459
const classes = getIconClasses (
@@ -444,8 +476,19 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
444
476
} ] : [ ] ) ;
445
477
templateData . size . innerText = node . element . size . toString ( ) ;
446
478
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
+ }
447
487
}
448
488
489
+ /**
490
+ * Does nothing (not implemented).
491
+ */
449
492
public disposeTemplate ( _templateData : DialogEntryData ) : void {
450
493
// throw new Error("Method not implemented.");
451
494
}
0 commit comments