-
Notifications
You must be signed in to change notification settings - Fork 9.8k
[file_selector] Add Windows support #4424
[file_selector] Add Windows support #4424
Conversation
High-level notes for review (most of what I said here for the macOS version applies):
|
fake_selected_file_2.file(), | ||
}; | ||
IShellItemArrayPtr fake_result_array; | ||
::SHCreateShellItemArrayFromIDLists(2, fake_selected_files, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're probably wondering why this setup is totally different than the other tests that return only one file. I.e., why not use ScopedTestShellItem
and then create an IShellItemArrayPtr
from those IShellItemPtr
s? And the answer is: because there's an API to create an IShellItemArray
from exactly one IShellItem
, and there's an API to create an IShellItemArray
from a list of ITEMIDLIST
s, but there's no API to create an IShellItemArray
from multiple IShellItem
s. Because Win32 😒
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stuartmorgan I think you should be calling the user-exposed methods of the plugin package in the example app. This is assuming that the frontend plugin is just a pass-through for method calls to the platform interface (which in this case it is, but in the case of Google Maps, for example, it isn't). |
This ends badly for reasons that aren't immediately obvious; it's on my list to document in the federated plugin playbook. I'll follow up in Discord since the scope is well beyond this plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sending out the comments so far to help get things moving. I've reviewed up to but not including file_selector_plugin_test.cpp
.
packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart
Outdated
Show resolved
Hide resolved
|
||
/// Widget that displays a text file in a dialog | ||
class TextDisplay extends StatelessWidget { | ||
/// Default Constructor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Here and on a few others (e.g. directoryPath below), the comment is probably unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have the default analysis settings on, which require docs for anything public. It's easier to just put the not-very-helpful comment than to disable the warning for the line (and I think we should leave it on in general since it's example code).
I did a pass over the member variables and tried to give them somewhat more useful comments.
packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart
Outdated
Show resolved
Hide resolved
packages/file_selector/file_selector_windows/example/lib/open_text_page.dart
Outdated
Show resolved
Hide resolved
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; | ||
import 'package:flutter/material.dart'; | ||
|
||
/// Page for showing an example of saving with file_selector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: for consistency use 'Screen' here or 'Page' across the board. Not sure which I prefer, personally -- both seem fine :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed to match the others.
packages/file_selector/file_selector_windows/example/windows/CMakeLists.txt
Outdated
Show resolved
Hide resolved
|
||
FileDialogController::FileDialogController(IFileDialog* dialog) | ||
: dialog_(dialog) { | ||
dialog_->AddRef(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing to do here, but I wonder if long-term it might be useful to use ComPtr for these sorts of cases -- this one and the local below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I smartened all of the pointers in the plugin.
// From x_type_group.dart | ||
// Only 'extensions' are supported by Windows for filtering. | ||
const char kTypeGroupLabelKey[] = "label"; | ||
const char kTypeGroupExtensionsKey[] = "extensions"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: these could all be constexpr
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, still working off that muscle memory :)
// empty string on failure. | ||
std::string GetPathForShellItem(IShellItem *shell_item) { | ||
wchar_t *wide_path = nullptr; | ||
if (!SUCCEEDED(shell_item->GetDisplayName(SIGDN_FILESYSPATH, &wide_path))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding null-check bailout on shell_item
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
std::unique_ptr<FileDialogController> CreateController( | ||
IFileDialog *dialog) const override { | ||
return std::make_unique<FileDialogController>(dialog); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding an assert not null here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright - done! Looks good modulo mostly nits.
fake_selected_file_2.file(), | ||
}; | ||
IShellItemArrayPtr fake_result_array; | ||
::SHCreateShellItemArrayFromIDLists(2, fake_selected_files, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// the instance goes out of scope. | ||
// | ||
// This creates a file on the filesystem since creating IShellItem instances for | ||
// files that don't exist is not-trivial. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pedantic nit: non-trivial or not trivial :P
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that was a typo. Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm afraid I added some more files to review: while updating for the review comments I realized I hadn't come back to update this PR to reflect the decision to switch plugins to using internal method channels. So I've added file_selector_windows.dart and file_selector_windows_test.dart, which for now are barely-edited copies of the shared method channel that it was using before. Later it can be adjusted to be better tailored to match the native Windows code to simplify the native side.
(I also noticed that some C++ files had the wrong pointer alignment as the prevailing style, and fixed those.)
packages/file_selector/file_selector_windows/example/lib/get_directory_page.dart
Outdated
Show resolved
Hide resolved
|
||
/// Widget that displays a text file in a dialog | ||
class TextDisplay extends StatelessWidget { | ||
/// Default Constructor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have the default analysis settings on, which require docs for anything public. It's easier to just put the not-very-helpful comment than to disable the warning for the line (and I think we should leave it on in general since it's example code).
I did a pass over the member variables and tried to give them somewhat more useful comments.
packages/file_selector/file_selector_windows/example/lib/open_multiple_images_page.dart
Outdated
Show resolved
Hide resolved
packages/file_selector/file_selector_windows/example/lib/open_text_page.dart
Outdated
Show resolved
Hide resolved
import 'package:file_selector_platform_interface/file_selector_platform_interface.dart'; | ||
import 'package:flutter/material.dart'; | ||
|
||
/// Page for showing an example of saving with file_selector |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed to match the others.
|
||
FileDialogController::FileDialogController(IFileDialog* dialog) | ||
: dialog_(dialog) { | ||
dialog_->AddRef(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I smartened all of the pointers in the plugin.
// From x_type_group.dart | ||
// Only 'extensions' are supported by Windows for filtering. | ||
const char kTypeGroupLabelKey[] = "label"; | ||
const char kTypeGroupExtensionsKey[] = "extensions"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, still working off that muscle memory :)
// empty string on failure. | ||
std::string GetPathForShellItem(IShellItem *shell_item) { | ||
wchar_t *wide_path = nullptr; | ||
if (!SUCCEEDED(shell_item->GetDisplayName(SIGDN_FILESYSPATH, &wide_path))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
std::unique_ptr<FileDialogController> CreateController( | ||
IFileDialog *dialog) const override { | ||
return std::make_unique<FileDialogController>(dialog); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
// the instance goes out of scope. | ||
// | ||
// This creates a file on the filesystem since creating IShellItem instances for | ||
// files that don't exist is not-trivial. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that was a typo. Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Brings file_selector_windows into flutter/plugins from FDE, with the following changes:
(See [file_selector] Implement for Windows #3265 for some background)
Does not currently include native UI tests to allow for end-to-end testing (since it shows native UI Flutter integration tests can't be used). They should be added later, but the unit tests give substantial coverage, making it substantially better to move the plugin now to get those tests running.
(In the future, this could potentially be replaced by an FFI-based implementation, but this version allows us to leverage existing, field-tested code rather than starting from scratch.)
Windows portion of flutter/flutter#70221
Pre-launch Checklist
dart format
.)[shared_preferences]
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.