Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[file_selector] Add Windows support #4424

Merged
merged 25 commits into from
Jan 26, 2022

Conversation

stuartmorgan-g
Copy link
Contributor

Brings file_selector_windows into flutter/plugins from FDE, with the following changes:

  • Non-trivially refactored to allow for unit tests of almost all of the native code
  • Added native unit test coverage
  • Added an in-package example (almost an exact duplicate of the app-facing version, but written against the platform interface, as is our current practice)
    (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

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the relevant style guides and ran the auto-formatter. (Note that unlike the flutter/flutter repo, the flutter/plugins repo does use dart format.)
  • I signed the CLA.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@stuartmorgan-g
Copy link
Contributor Author

High-level notes for review (most of what I said here for the macOS version applies):

  • The example is pretty much just a copy of the existing example + boilerplate. It's exactly the macOS example + boilerplate since they both use the platform interface APIs
  • If you do incremental review based on the initial copy-from-FDE commit:
    • string_utils.cpp is just moved code
    • file_selector_plugin.cpp is basically the initial plugin code moved to a new file, just adjusted sightly to use FileDialogController.
    • the CMake changes are the same as what was done for url_launcher_windows
    • file_dialog_controller.* is new code to allow for unit testing. It's pretty straightforward since it's as minimal as it can be by design.
    • there's a bunch of new test utility code because actually getting the tests set up is non-trivial. Hopefully each piece is relatively straightforward to understand though.

@stuartmorgan-g stuartmorgan-g removed the request for review from ditman October 11, 2021 19:03
fake_selected_file_2.file(),
};
IShellItemArrayPtr fake_result_array;
::SHCreateShellItemArrayFromIDLists(2, fake_selected_files,
Copy link
Contributor Author

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 IShellItemPtrs? 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 ITEMIDLISTs, but there's no API to create an IShellItemArray from multiple IShellItems. Because Win32 😒

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

@ditman
Copy link
Member

ditman commented Oct 11, 2021

written against the platform interface, as is our current practice

@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).

@stuartmorgan-g
Copy link
Contributor Author

@stuartmorgan I think you should be calling the user-exposed methods of the plugin package in the example app.

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.

@godofredoc godofredoc changed the base branch from master to main January 6, 2022 22:45
Copy link
Member

@cbracken cbracken left a 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.


/// Widget that displays a text file in a dialog
class TextDisplay extends StatelessWidget {
/// Default Constructor
Copy link
Member

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.

Copy link
Contributor Author

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.

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
Copy link
Member

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 :)

Copy link
Contributor Author

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();
Copy link
Member

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.

Copy link
Contributor Author

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";
Copy link
Member

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.

Copy link
Contributor Author

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))) {
Copy link
Member

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.

Copy link
Contributor Author

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);
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@cbracken cbracken left a 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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

// 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.
Copy link
Member

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

Copy link
Contributor Author

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.

@cbracken cbracken mentioned this pull request Jan 25, 2022
11 tasks
Copy link
Contributor Author

@stuartmorgan-g stuartmorgan-g left a 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.)


/// Widget that displays a text file in a dialog
class TextDisplay extends StatelessWidget {
/// Default Constructor
Copy link
Contributor Author

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.

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
Copy link
Contributor Author

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();
Copy link
Contributor Author

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";
Copy link
Contributor Author

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))) {
Copy link
Contributor Author

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);
Copy link
Contributor Author

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.
Copy link
Contributor Author

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.

Copy link
Member

@cbracken cbracken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM stamp from a Japanese personal seal

@stuartmorgan-g stuartmorgan-g added the waiting for tree to go green (Use "autosubmit") This PR is approved and tested, but waiting for the tree to be green to land. label Jan 26, 2022
@fluttergithubbot fluttergithubbot merged commit cd97c19 into flutter:main Jan 26, 2022
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Jan 26, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
cla: yes p: file_selector platform-windows waiting for tree to go green (Use "autosubmit") This PR is approved and tested, but waiting for the tree to be green to land.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants