Skip to content

Conversation

@mattkae
Copy link
Contributor

@mattkae mattkae commented Oct 24, 2024

This pull request only makes changes to the windows embedder API and wires a flag through the framework. This does not include any user-facing API changes.

Design doc: https://docs.google.com/document/d/1eQG-IS7r4_S9_h50MY_hSGwUVtgSTRSLzu7MLzPNd2Y/edit?tab=t.0

To test

  1. Check out this branch in canonical/flutter (this includes changes to the framework which are not included here on purpose for reviewer's sake!)
  2. Build the engine
  3. Then run the multi window reference application:
    cd examples/multi_window_ref_app/
    flutter run --debug --local-engine-src-path <PATH_TO_ENGINE_SRC> --local-engine host_debug_unopt --local-engine-host host_debug_unopt .\lib\main.dart --enable-multi-window
    

Don't forget the --enable-multi-window flag or it will not work

What's new?

Framework

  • Added a new --enable-multi-window flag to turn on multi window features

Engine

  • Implemented regular windows in the windows embedder

image

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 [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!).

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@google-cla
Copy link

google-cla bot commented Oct 24, 2024

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@justinmc
Copy link
Contributor

@mattkae Should we put all the subsequent PRs in draft mode for now (assuming they are not yet ready for review)? Just so other triagers don't get confused.

Thanks for all of this work by the way!

CC @loic-sharma @cbracken Any idea who should take on getting all of this reviewed?

@mattkae
Copy link
Contributor Author

mattkae commented Oct 31, 2024

@mattkae Should we put all the subsequent PRs in draft mode for now (assuming they are not yet ready for review)? Just so other triagers don't get confused.

If you would prefer that, I can put the others in draft mode to avoid confusion 👍

Thanks for all of this work by the way!

No problem :) Looking forward to working towards landing it !

@loic-sharma
Copy link
Member

CC @loic-sharma @cbracken Any idea who should take on getting all of this reviewed?

I suspect @goderbauer would be the best person to review once he's back :)

@Piinks
Copy link
Contributor

Piinks commented Nov 7, 2024

Hey @mattkae, welcome, what a first PR! 🎊 We’re excited to see all the progress on the multi-window project, thank you! Sharing from an offline sync for folks following these developments:

Before reviewing these pull requests, we’re going to ensure we finish the design review so we are aligned on the final APIs and where they will live. Please see the linked design document and provide feedback.

We’ll discuss this feedback in a future Dash Forum, these typically occur weekly and are open to all members of flutter-hackers.

The date for this forum has not been set yet, as we are waiting for one of the reviewers to return from being out of office. Once they are back, we will set a date and communicate it on the Discord as usual.

Thanks very much for your patience, and for all of the work here!

@flutter-dashboard
Copy link

This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@mattkae
Copy link
Contributor Author

mattkae commented Dec 12, 2024

@goderbauer @loic-sharma If you are interested, this PR now reflect "declarative API" that we settled on in the spec. The tests still need to be updated, but this should give you a sense of the general look-and-feel of the API as we've described it so far in the document.

@github-actions github-actions bot added the tool Affects the "flutter" command-line tool. See also t: labels. label Dec 16, 2024
std::optional<WindowState> state;
};

} // namespace flutter
Copy link
Member

Choose a reason for hiding this comment

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

I don't know how I feel about this. Especially when looking at later PRs, which seem to put quite a bit of additional logic here, like positioning for popovers. Yes, we share some code between embedders already, but this seems very different as it implements a specific algorithm.

We have formal embedder API, "informal" platform channels, and now we have algorithms that embedders are expected to implement, placed at arbitrary location in the engine. What about out of tree embedders?

Wouldn't it be better if something like this was implemented in dart instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

In the case of positioning, modern compositors do this themselves, as an application never has all the context required to do this correctly (or is not allowed the context to maintain application isolation). For compositors that do not do this, the application needs to perform this, thus each compositor needs to implement this logic.

The difficulty in making shared dart code is the embedder is already running native code when it needs it. If we were to make this code run before requests reach the embedder it would make the API more complicated. In all cases, the shared code is more of a set of helper functions, as the behaviour always depends on the compositor in use.

Copy link
Member

Choose a reason for hiding this comment

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

I don't quite understand how this addresses my concern. There is no precedent for sharing logic between embedders in-tree like this and I think this definitely needs further discussion.

I have proposal for synchronous ffi-based API https://docs.flutter.dev/go/multi-window-api-ffi. With synchronous API we can easily move all this to Dart code so that it can be accessed by all embedders, not just the "blessed" ones.

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this not similar to the shared text input model code?

Copy link
Member

@knopp knopp Mar 3, 2025

Choose a reason for hiding this comment

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

Very vaguely. TextInputModel doesn't implement particular algorithm, at least not in a way where all embedders rely on. For example on macOS we're only using a tiny subset of it. iOS or Android is not using it at all. So it's more of a utility class rather than defining logic that embedders are supposed to follow.

Also text input is one of the most problematic parts of Flutter, and if we were starting from scratch we would have likely chosen a very different approach, see #150460. So modeling new code after text input doesn't seem like the best idea.

Copy link

Choose a reason for hiding this comment

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

That's actually how this is meant to be used - as helpers - depending on platform, the embedder might use e.g. the positioner information itself (like on Windows) - on others (like Wayland) that context will be passed on verbatim to the compositor, so there's no point in making those calculations in Flutter at all. On others yet, child windows might not be supported altogether (mobile platforms, automotive).

The implementation here will only be shared between platforms that can meaningfully make use of it (floating window desktops - which sure, are the majority of them, but not all). Other platforms should default to not supporting child windows until/unless they grow support for at least a subset of them.

map.insert(
{EncodableValue(kSizeKey),
EncodableValue(EncodableList{EncodableValue(size->width()),
EncodableValue(size->height())})});
Copy link
Contributor

Choose a reason for hiding this comment

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

How does this compare with FlutterEngineSendWindowMetricsEvent? Can the size be different to the size of the view (i.e. the Flutter widget is combined with other native widgets). Can the application make use of the size information?

Copy link
Contributor

Choose a reason for hiding this comment

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

They are essentially the same size. onWindowChanged sends the size of the client rectangle of the top-level window, which corresponds to the size of root view in logical coordinates.

Copy link
Contributor

@robert-ancell robert-ancell left a comment

Choose a reason for hiding this comment

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

My Windows knowledge is pretty limited, but the code looks good to me. The only serious thing I noticed is the interaction with the window size and the view size, which might just need some clarification to ensure developers don't mix up the two.

@knopp
Copy link
Member

knopp commented Mar 3, 2025

What is the reason for trying to merge this without the appropriate framework API? This is tightly coupled to added framework API, I don't quite understand how this can be reviewed and merged separately before approving the framework part.

@mattkae
Copy link
Contributor Author

mattkae commented Mar 3, 2025

What is the reason for trying to merge this without the appropriate framework API? This is tightly coupled to added framework API, I don't quite understand how this can be reviewed and merged separately before approving the framework part.

The reason is that the Google folks don't want to land a public API until (1) all multi-view bugs have been fixed and (2) we are certain that the API changes are more-or-less final.

@loic-sharma can provide more details, but we settled on this in the end to make things move faster :)

WPARAM wparam,
LPARAM lparam) {
switch (message) {
case WM_NCDESTROY: {
Copy link
Member

Choose a reason for hiding this comment

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

Window destroying flow probably needs some thought? A very common scenario is displaying a prompt when closing the window (i.e. to prevent closing unsaved documents).

Copy link

@weltkante weltkante Mar 3, 2025

Choose a reason for hiding this comment

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

WM_NCDESTROY is just for cleanup of resources after closing, the window is already closed and being torn down, so handling this doesn't imply any handling of closing scenarios and is just about resource management

Copy link
Member

Choose a reason for hiding this comment

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

I meant the overall flow. For example destroying the window from Flutter involoves sending WM_CLOSE. But WM_CLOSE is the one message we should actually handle, prevent default and notify the framework so that we can give the user a way to prevent closing of windows if needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

WM_CLOSE is handled by the app lifecycle manager, but it only works for the last window. We expected to have a proper window lifecycle manager for this. Anyway, the delegate pattern you're using in the FFI branch seems like a nice, cleaner alternative.

@knopp
Copy link
Member

knopp commented Mar 4, 2025

Here is my work in progress branch based on this work but converting it to ffi: https://github.com/knopp/flutter/tree/windows_multi_window_ffi

It's bit rough but it mostly works.

If you compare the window.dart to original you will notice that it doesn't need any futures, isReady checks or future builders.

It also doesn't need to need to sync state from native (i.e. size). There is only one source of truth and that is whatever winapi provides at that moment.

The prototype also allows handling win32 messages in dart code. Down the line this would allow for packages to customize window behavior, i.e. overriding WM_NCHITTEST which is required to make custom frame windows.

hbatagelo and others added 7 commits March 6, 2025 14:59
#37)

This simplifies the logic for focusing the root view when the top-level
window is activated. It removes the handling of `WM_MOUSEACTIVATE`, as
`WM_ACTIVATE` already accounts for activations via mouse clicks.
Additionally, it ensures that the view is not focused when the top-level
window is being deactivated.
@hbatagelo
Copy link
Contributor

My Windows knowledge is pretty limited, but the code looks good to me. The only serious thing I noticed is the interaction with the window size and the view size, which might just need some clarification to ensure developers don't mix up the two.

Both the requested window size and the size notified by onWindowChanged refer to the client rectangle size, which matches the root view size. The application is unaware of the window frame size or the window rectangle size (frame + drop shadow).

I added an extra comment to the SendOnWindowChanged declaration for clarity.

@knopp
Copy link
Member

knopp commented Mar 11, 2025

I think down the line we want to refer to size as clientSize or contentSize (I'd vote for contentSize). Some platforms support setting frame size too, and we will want to expose it, even if it's per platform specific call. So it would not good to get rid of ambiguity from beginning.

@flutter-dashboard
Copy link

It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging.

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.If you believe this PR qualifies for a test exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!). The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@wanjm
Copy link

wanjm commented Mar 27, 2025

how much work left before it can be merged?

@Saviq
Copy link

Saviq commented Mar 27, 2025

@wanjm there's a new approach to crossing the Dart / native boundary in the works:

#165072
https://docs.google.com/document/d/1UEaxd_aIlsPMzkfB6oyeYaKzwHadrWGyMzOS33oSbK4/edit

Not too long! We have agreement on the Dart API, multi-view prerequisites mostly dealt with, just need to work things out on the three desktop platforms so we're confident with the solution.

@GroovinChip
Copy link
Contributor

@wanjm there's a new approach to crossing the Dart / native boundary in the works:

#165072 https://docs.google.com/document/d/1UEaxd_aIlsPMzkfB6oyeYaKzwHadrWGyMzOS33oSbK4/edit

Not too long! We have agreement on the Dart API, multi-view prerequisites mostly dealt with, just need to work things out on the three desktop platforms so we're confident with the solution.

Is the plan to move over to this approach, or to continue on with method channels until the FFI approach is finalized and ready? If the plan is to switch to FFI now, does that impact the time until multi-window can be delivered?

@Saviq
Copy link

Saviq commented Apr 3, 2025

Is the plan to move over to this approach, or to continue on with method channels until the FFI approach is finalized and ready? If the plan is to switch to FFI now, does that impact the time until multi-window can be delivered?

Plan is to go for FFI, no point introducing a lot of code that will be dropped soon. Yes, it does mean it's delayed some weeks. But it's closer than you might think: #165072.

@GroovinChip
Copy link
Contributor

Thanks for the reply! Glad the delay will only be a few weeks.

@mattkae
Copy link
Contributor Author

mattkae commented May 22, 2025

The implementation of multi-window in Flutter has drifted a lot since this PR was opened. Please see #168728 for the latest implementation. We're currently focusing on an FFI-based approach in contrast to the async method channels approach that we implemented here.

@mattkae mattkae closed this May 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop engine flutter/engine related. See also e: labels. platform-windows Building on or for Windows specifically tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.