@@ -15,16 +15,71 @@ using namespace winrt::Windows::UI::Xaml::Data;
1515using namespace winrt ::Windows::UI::Xaml::Navigation;
1616using namespace winrt ::Windows::Foundation;
1717using namespace winrt ::Windows::Foundation::Collections;
18- using namespace winrt ::Windows::Storage;
19- using namespace winrt ::Windows::Storage::AccessCache;
20- using namespace winrt ::Windows::Storage::Pickers;
2118using namespace winrt ::Microsoft::Terminal::Settings::Model;
2219
2320static const std::array<winrt::guid, 2 > InBoxProfileGuids{
2421 winrt::guid{ 0x61c54bbd , 0xc2c6 , 0x5271 , { 0x96 , 0xe7 , 0x00 , 0x9a , 0x87 , 0xff , 0x44 , 0xbf } }, // Windows Powershell
2522 winrt::guid{ 0x0caa0dad , 0x35be , 0x5f56 , { 0xa8 , 0xff , 0xaf , 0xce , 0xee , 0xaa , 0x61 , 0x01 } } // Command Prompt
2623};
2724
25+ // Function Description:
26+ // - This function presents a File Open "common dialog" and returns its selected file asynchronously.
27+ // Parameters:
28+ // - customize: A lambda that receives an IFileDialog* to customize.
29+ // Return value:
30+ // (async) path to the selected item.
31+ template <typename TLambda>
32+ static winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenFilePicker (TLambda&& customize)
33+ {
34+ auto fileDialog{ winrt::create_instance<IFileDialog>(CLSID_FileOpenDialog) };
35+ DWORD flags{};
36+ THROW_IF_FAILED (fileDialog->GetOptions (&flags));
37+ THROW_IF_FAILED (fileDialog->SetOptions (flags | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR | FOS_DONTADDTORECENT)); // filesystem objects only; no recent places
38+ customize (fileDialog.get ());
39+
40+ auto hr{ fileDialog->Show (NULL ) };
41+ if (!SUCCEEDED (hr))
42+ {
43+ if (hr == HRESULT_FROM_WIN32 (ERROR_CANCELLED))
44+ {
45+ co_return winrt::hstring{};
46+ }
47+ THROW_HR (hr);
48+ }
49+
50+ winrt::com_ptr<IShellItem> result;
51+ THROW_IF_FAILED (fileDialog->GetResult (result.put ()));
52+
53+ wil::unique_cotaskmem_string filePath;
54+ THROW_IF_FAILED (result->GetDisplayName (SIGDN_FILESYSPATH, &filePath));
55+
56+ co_return winrt::hstring{ filePath.get () };
57+ }
58+
59+ // Function Description:
60+ // - Helper that opens a file picker pre-seeded with image file types.
61+ static winrt::Windows::Foundation::IAsyncOperation<winrt::hstring> OpenImagePicker ()
62+ {
63+ static constexpr COMDLG_FILTERSPEC supportedImageFileTypes[] = {
64+ { L" All Supported Bitmap Types (*.jpg, *.jpeg, *.png, *.bmp, *.gif, *.tiff, *.ico)" , L" *.jpg;*.jpeg;*.png;*.bmp;*.gif;*.tiff;*.ico" },
65+ { L" All Files (*.*)" , L" *.*" }
66+ };
67+
68+ static constexpr winrt::guid clientGuidImagePicker{ 0x55675F54 , 0x74A1 , 0x4552 , { 0xA3 , 0x9D , 0x94 , 0xAE , 0x85 , 0xD8 , 0xF2 , 0x7A } };
69+ return OpenFilePicker ([](auto && dialog) {
70+ THROW_IF_FAILED (dialog->SetClientGuid (clientGuidImagePicker));
71+ try
72+ {
73+ auto pictureFolderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_PicturesLibrary, KF_FLAG_DEFAULT, nullptr ) };
74+ dialog->SetDefaultFolder (pictureFolderShellItem.get ());
75+ }
76+ CATCH_LOG (); // non-fatal
77+ THROW_IF_FAILED (dialog->SetFileTypes (ARRAYSIZE (supportedImageFileTypes), supportedImageFileTypes));
78+ THROW_IF_FAILED (dialog->SetFileTypeIndex (1 )); // the array is 1-indexed
79+ THROW_IF_FAILED (dialog->SetDefaultExtension (L" jpg;jpeg;png;bmp;gif;tiff;ico" ));
80+ });
81+ }
82+
2883namespace winrt ::Microsoft::Terminal::Settings::Editor::implementation
2984{
3085 Windows::Foundation::Collections::IObservableVector<Editor::Font> ProfileViewModel::_MonospaceFontList{ nullptr };
@@ -582,74 +637,74 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
582637 {
583638 auto lifetime = get_strong ();
584639
585- FileOpenPicker picker;
586-
587- _State.WindowRoot ().TryPropagateHostingWindow (picker); // if we don't do this, there's no HWND for it to attach to
588- picker.ViewMode (PickerViewMode::Thumbnail);
589- picker.SuggestedStartLocation (PickerLocationId::PicturesLibrary);
590-
591- // Converted into a BitmapImage. This list of supported image file formats is from BitmapImage documentation
592- // https://docs.microsoft.com/en-us/uwp/api/Windows.UI.Xaml.Media.Imaging.BitmapImage?view=winrt-19041#remarks
593- picker.FileTypeFilter ().ReplaceAll ({ L" .jpg" , L" .jpeg" , L" .png" , L" .bmp" , L" .gif" , L" .tiff" , L" .ico" });
594-
595- StorageFile file = co_await picker.PickSingleFileAsync ();
596- if (file != nullptr )
640+ auto file = co_await OpenImagePicker ();
641+ if (!file.empty ())
597642 {
598- _State.Profile ().BackgroundImagePath (file. Path () );
643+ _State.Profile ().BackgroundImagePath (file);
599644 }
600645 }
601646
602647 fire_and_forget Profiles::Icon_Click (IInspectable const &, RoutedEventArgs const &)
603648 {
604649 auto lifetime = get_strong ();
605650
606- FileOpenPicker picker;
607-
608- _State.WindowRoot ().TryPropagateHostingWindow (picker); // if we don't do this, there's no HWND for it to attach to
609- picker.ViewMode (PickerViewMode::Thumbnail);
610- picker.SuggestedStartLocation (PickerLocationId::PicturesLibrary);
611-
612- // Converted into a BitmapIconSource. This list of supported image file formats is from BitmapImage documentation
613- // https://docs.microsoft.com/en-us/uwp/api/Windows.UI.Xaml.Media.Imaging.BitmapImage?view=winrt-19041#remarks
614- picker.FileTypeFilter ().ReplaceAll ({ L" .jpg" , L" .jpeg" , L" .png" , L" .bmp" , L" .gif" , L" .tiff" , L" .ico" });
615-
616- StorageFile file = co_await picker.PickSingleFileAsync ();
617- if (file != nullptr )
651+ auto file = co_await OpenImagePicker ();
652+ if (!file.empty ())
618653 {
619- _State.Profile ().Icon (file. Path () );
654+ _State.Profile ().Icon (file);
620655 }
621656 }
622657
623658 fire_and_forget Profiles::Commandline_Click (IInspectable const &, RoutedEventArgs const &)
624659 {
625660 auto lifetime = get_strong ();
626661
627- FileOpenPicker picker;
662+ static constexpr COMDLG_FILTERSPEC supportedFileTypes[] = {
663+ { L" Executable Files (*.exe, *.cmd, *.bat)" , L" *.exe;*.cmd;*.bat" },
664+ { L" All Files (*.*)" , L" *.*" }
665+ };
628666
629- _State.WindowRoot ().TryPropagateHostingWindow (picker); // if we don't do this, there's no HWND for it to attach to
630- picker.ViewMode (PickerViewMode::Thumbnail);
631- picker.SuggestedStartLocation (PickerLocationId::ComputerFolder);
632- picker.FileTypeFilter ().ReplaceAll ({ L" .bat" , L" .exe" , L" .cmd" });
667+ static constexpr winrt::guid clientGuidExecutables{ 0x2E7E4331 , 0x0800 , 0x48E6 , { 0xB0 , 0x17 , 0xA1 , 0x4C , 0xD8 , 0x73 , 0xDD , 0x58 } };
668+ auto path = co_await OpenFilePicker ([](auto && dialog) {
669+ THROW_IF_FAILED (dialog->SetClientGuid (clientGuidExecutables));
670+ try
671+ {
672+ auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr ) };
673+ dialog->SetDefaultFolder (folderShellItem.get ());
674+ }
675+ CATCH_LOG (); // non-fatal
676+ THROW_IF_FAILED (dialog->SetFileTypes (ARRAYSIZE (supportedFileTypes), supportedFileTypes));
677+ THROW_IF_FAILED (dialog->SetFileTypeIndex (1 )); // the array is 1-indexed
678+ THROW_IF_FAILED (dialog->SetDefaultExtension (L" exe;cmd;bat" ));
679+ });
633680
634- StorageFile file = co_await picker.PickSingleFileAsync ();
635- if (file != nullptr )
681+ if (!path.empty ())
636682 {
637- _State.Profile ().Commandline (file. Path () );
683+ _State.Profile ().Commandline (path );
638684 }
639685 }
640686
641687 fire_and_forget Profiles::StartingDirectory_Click (IInspectable const &, RoutedEventArgs const &)
642688 {
643689 auto lifetime = get_strong ();
644- FolderPicker picker;
645- _State.WindowRoot ().TryPropagateHostingWindow (picker); // if we don't do this, there's no HWND for it to attach to
646- picker.SuggestedStartLocation (PickerLocationId::DocumentsLibrary);
647- picker.FileTypeFilter ().ReplaceAll ({ L" *" });
648- StorageFolder folder = co_await picker.PickSingleFolderAsync ();
649- if (folder != nullptr )
650- {
651- StorageApplicationPermissions::FutureAccessList ().AddOrReplace (L" PickedFolderToken" , folder);
652- _State.Profile ().StartingDirectory (folder.Path ());
690+ auto folder = co_await OpenFilePicker ([](auto && dialog) {
691+ static constexpr winrt::guid clientGuidFolderPicker{ 0xAADAA433 , 0xB04D , 0x4BAE , { 0xB1 , 0xEA , 0x1E , 0x6C , 0xD1 , 0xCD , 0xA6 , 0x8B } };
692+ THROW_IF_FAILED (dialog->SetClientGuid (clientGuidFolderPicker));
693+ try
694+ {
695+ auto folderShellItem{ winrt::capture<IShellItem>(&SHGetKnownFolderItem, FOLDERID_ComputerFolder, KF_FLAG_DEFAULT, nullptr ) };
696+ dialog->SetDefaultFolder (folderShellItem.get ());
697+ }
698+ CATCH_LOG (); // non-fatal
699+
700+ DWORD flags{};
701+ THROW_IF_FAILED (dialog->GetOptions (&flags));
702+ THROW_IF_FAILED (dialog->SetOptions (flags | FOS_PICKFOLDERS)); // folders only
703+ });
704+
705+ if (!folder.empty ())
706+ {
707+ _State.Profile ().StartingDirectory (folder);
653708 }
654709 }
655710
0 commit comments