Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propagate IslandWindow's HWND into any component that needs it #8391

Merged
merged 3 commits into from
Nov 30, 2020

Conversation

DHowett
Copy link
Member

@DHowett DHowett commented Nov 24, 2020

This fixes the issue with the settings UI where clicking the browse
buttons would cause an exception to be thrown when we tried to display a
picker without an originating HWND.

It turns out that pickers need a hosting/parent window, and Xaml Islands
doesn't furnish us with a CoreWindow that's set up for that use case.
Alas!

Raymond Chen's blog post on the matter suggests that we should
hand the HWND off through some classic COM interface. To do that
properly, Terminal's various components need to implement that interface
and propagate the HWND down where it's needed.

Thanks to a Xaml compiler issue, we can't actually do that. To work
around that, we've begged and borrowed different methods for pushing
HWNDs around:

  1. Using IInitializeWithWindow in secret
  2. A member that takes a uint64
  3. An interface that offers a function that will "wire up" the HWND.

I chose (1) because AppHost can implement IInitializeWithWindow, but
TerminalPage cannot. We're just pretending that TerminalPage can.

I chose (2) because none of the Xaml types in TerminalSettingsEditor can
implement the interface thanks to the aforementioned compiler issue, but
we don't have an escape hatch like AppHost that lives in the same module
and can help us do the propagation.

I chose (3) because I didn't want to commit the same sin as (2) seven
times
for every different type of settings page that exists. (3) is
backed by "IHostedInWindow", and anybody who knows they have to use
IInitializeWithWindow to tie an HWND to an object can call
IHostedInWindow.TryPropagateHostingWindow() on that object.

House of cards.

This fixes the issue with the settings UI where clicking the browse
buttons would cause an exception to be thrown when we tried to display a
picker without an originating HWND.

It turns out that pickers need a hosting/parent window, and Xaml Islands
doesn't furnish us with a CoreWindow that's set up for that use case.
Alas!

Raymond Chen's [blog post on the matter] suggests that we should
hand the HWND off through some classic COM interface. To do that
properly, Terminal's various components need to implement that interface
and propagate the HWND down where it's needed.

Thanks to a [Xaml compiler issue], we can't actually do that. To work
around that, we've begged and borrowed different methods for pushing
HWNDs around:

1. Using IInitializeWithWindow in secret
2. A member that takes a uint64
3. An interface that offers a function that will "wire up" the HWND.

I chose (1) because AppHost can implement IInitializeWithWindow, but
TerminalPage cannot. We're just pretending that TerminalPage _can_.

I chose (2) because none of the Xaml types in TerminalSettingsEditor can
implement the interface thanks to the aforementioned compiler issue, but
we don't have an escape hatch like AppHost that lives in the same module
and can help us do the propagation.

I chose (3) because I didn't want to commit the same sin as (2) _seven
times_ for every different type of settings page that exists. (3) is
backed by "IHostedInWindow", and anybody who knows they have to use
IInitializeWithWindow to tie an HWND to an object can call
IHostedInWindow.TryPropagateHostingWindow() on that object.

House of cards.

[Xaml compiler issue]: microsoft/microsoft-ui-xaml#3331
[blog post on the matter]: https://devblogs.microsoft.com/oldnewthing/20190412-00/?p=102413
@DHowett
Copy link
Member Author

DHowett commented Nov 24, 2020

image

@sylveon
Copy link

sylveon commented Nov 24, 2020

this is fine

Copy link
Member

@carlos-zamora carlos-zamora left a comment

Choose a reason for hiding this comment

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

Approving because you'll either resolve the two comments and I'll approve. Or say nah, and I'll approve anyways, then do them myself in a separate PR haha. The core of this PR is finding a way to get around the crashing browse buttons anyways, and I'm all in for that. Thanks for doing this!

// TODO GH#1564: Settings UI
// This crashes on click, for some reason
//fire_and_forget StartingDirectory_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
fire_and_forget StartingDirectory_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::RoutedEventArgs const& e);
Copy link
Member

Choose a reason for hiding this comment

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

Mind adding and hooking up the button for Icon, while you're here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Rather not.

Comment on lines 60 to 64
//TODO: SETTINGS UI Commandline handling should be robust and intelligent
_State.WindowRoot().TryPropagateHostingWindow(picker); // if we don't do this, there's no HWND for it to attach to
picker.ViewMode(PickerViewMode::Thumbnail);
picker.SuggestedStartLocation(PickerLocationId::ComputerFolder);
picker.FileTypeFilter().ReplaceAll({ L".bat", L".exe" });
Copy link
Member

Choose a reason for hiding this comment

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

You could probably remove this TODO comment. Feel free to add any other file type filters 😊

Copy link
Member Author

Choose a reason for hiding this comment

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

No, this is neither robust nor intelligent and adding more file extension filters will not help it do so 😉

@DHowett
Copy link
Member Author

DHowett commented Nov 25, 2020

Want to make sure @zadjii-msft signs off on this, because it changes TerminalApp outside of SUI.

Copy link
Member

@zadjii-msft zadjii-msft left a comment

Choose a reason for hiding this comment

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

this is so incredibly horrifying that I'm sure this is the only way of doing this right now.

@DHowett DHowett changed the title Propagate the IslandWindow's HWND down into any component that needs it Propagate IslandWindow's HWND into any component that needs it Nov 30, 2020
@DHowett DHowett merged commit f9fc986 into feature/settings-ui Nov 30, 2020
@DHowett DHowett deleted the dev/duhowett/sui-browse-butts branch November 30, 2020 19:51
DHowett added a commit that referenced this pull request Nov 30, 2020
This fixes the issue with the settings UI where clicking the browse
buttons would cause an exception to be thrown when we tried to display a
picker without an originating HWND.

It turns out that pickers need a hosting/parent window, and Xaml Islands
doesn't furnish us with a CoreWindow that's set up for that use case.
Alas!

Raymond Chen's [blog post on the matter] suggests that we should
hand the HWND off through some classic COM interface. To do that
properly, Terminal's various components need to implement that interface
and propagate the HWND down where it's needed.

Thanks to a [Xaml compiler issue], we can't actually do that. To work
around that, we've begged and borrowed different methods for pushing
HWNDs around:

1. Using IInitializeWithWindow in secret
2. A member that takes a uint64
3. An interface that offers a function that will "wire up" the HWND.

I chose (1) because AppHost can implement IInitializeWithWindow, but
TerminalPage cannot. We're just pretending that TerminalPage _can_.

I chose (2) because none of the Xaml types in TerminalSettingsEditor can
implement the interface thanks to the aforementioned compiler issue, but
we don't have an escape hatch like AppHost that lives in the same module
and can help us do the propagation.

I chose (3) because I didn't want to commit the same sin as (2) _seven
times_ for every different type of settings page that exists. (3) is
backed by "IHostedInWindow", and anybody who knows they have to use
IInitializeWithWindow to tie an HWND to an object can call
IHostedInWindow.TryPropagateHostingWindow() on that object.

House of cards.

[Xaml compiler issue]: microsoft/microsoft-ui-xaml#3331
[blog post on the matter]: https://devblogs.microsoft.com/oldnewthing/20190412-00/?p=102413

(cherry picked from commit f9fc986)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants