-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Fix refresh-related crashes in Settings UI #8773
Conversation
const auto& firstItem{ menuItems.GetAt(0) }; | ||
SettingsNav().SelectedItem(firstItem); | ||
if (const auto tag{ SettingsNav().SelectedItem().as<MUX::Controls::NavigationViewItem>().Tag() }) | ||
if (const auto& tag{ SettingsNav().SelectedItem().try_as<MUX::Controls::NavigationViewItem>().Tag() }) |
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.
don't go too crazy with this -- some of these things generate temporaries and we don't want to capture references to those. Those should not have the & if we can help it.
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 ones you create from try_as, and the ones you create from GetAt, probably aren't safe to take references to. The ones you create from iteration over an iterator, however, are safe to take a reference to.
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 apologize - i know i asked you to flip it one way and then the other, but... some nuance 😄
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.
Ok. So just to get this straight, try_as
and GetAt
aren't safe to take refs of because MenuItems
might change from underneath us (especially when looking at the selected item stuff). So it's safer to take a copy so that we can use it later. Right?
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.
Ok. So just to get this straight,
try_as
andGetAt
aren't safe to take refs of becauseMenuItems
might change from underneath us (especially when looking at the selected item stuff). So it's safer to take a copy so that we can use it later. Right?
So, it's a little more complicated than that.
If this were just plain C++, that would be why. For a vector, at
returns a reference to the memory location inside the vector's storage, which is durable until the vector changes.
For C++/WinRT, the story is a little different. Sorta. I still don't totally know the C++ reasons for why this works the way it does.
GetAt()
returns a new object that was constructed to hold the internal WinRT object pointer. try_as
does the same thing. Those objects are projected type wrappers, and they are constructed on return from the function. You can't take a reference to them, because they are not referenceable. They don't exist.
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.
HOWEVER, I think I led you astray. I did some reading on Stack Overflow, and I finally understand it. What you had written before was completely legal, and now I understand why. You can safely revert the code that removed all the &
s.
Here's the skinny, on Herb Sutter's blog.
C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error.
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.
love it love it
if (profileTag->Guid() == selectedItemProfileTag->Guid()) | ||
{ | ||
// found the one that was selected before the refresh | ||
SettingsNav().SelectedItem(item); | ||
_Navigate(*selectedItemProfileTag); | ||
co_return; | ||
} | ||
} |
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.
hey how will this work when you have BASE LAYER selected?
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.
Base Layer is unique in that it has a string tag. So that's covered in the other if-statement.
@@ -180,6 +203,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation | |||
{ | |||
if (const auto clickedItemContainer = args.InvokedItemContainer()) | |||
{ | |||
if (clickedItemContainer.IsSelected()) |
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.
clever
// fallback to first menu item | ||
const auto& firstItem{ menuItems.GetAt(0) }; | ||
SettingsNav().SelectedItem(firstItem); | ||
if (const auto& tag{ SettingsNav().SelectedItem().try_as<MUX::Controls::NavigationViewItem>().Tag() }) | ||
{ | ||
_Navigate(unbox_value<hstring>(tag)); |
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.
oh I see, yea this works because the first one is always a string. gotcha.
Hello @carlos-zamora! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
Upon a settings reload, we would select the correct navigation item for a profile, but navigate to the old one. As a result, you would navigate to the old page that points to a dead profile object. This would make it appear like you did not discard/save the changes. This bugfix navigates to the newly created profile, ensuring that your changes are actually applied to the settings model's clone in use. ## References #8773 - Introduced the bug #6800 - Settings UI Epic ## Validation Steps Performed - Navigate to "powershell" profile - edit "tab title" value - discard changes Before: changes would persist unless you discarded changes again Now: changes are discarded Also verified expected behavior occurs when you click "save" instead of "discard"
Removes the visibility hack in `UpdateSettings` where we were hiding Profile menu items instead of removing them. This hack was removed using `ReplaceAll`. For an unknown reason, calling `Remove()` would result in an out-of-bounds error in XAML code. The "Discard" button would improperly refresh the Settings UI. Both of the bugs were caused by holding a reference to a hidden menu item then trying to set the `SelectedItem` to that menu item. Additionally, 9283375 adds a check for the selected item in `SettingsNav_ItemInvoked()`. This prevents navigation to an already selected item. This was the heuristic used by the XAML Controls Gallery. References microsoft#6800 - Settings UI Epic ## Validation Steps Performed (Repeated for each menu item) 1. Select the menu item 2. click "Discard changes" 3. Verify navigated to same page Also performed repro steps for microsoft#8747 and microsoft#8748. Closes microsoft#8747 Closes microsoft#8748
Upon a settings reload, we would select the correct navigation item for a profile, but navigate to the old one. As a result, you would navigate to the old page that points to a dead profile object. This would make it appear like you did not discard/save the changes. This bugfix navigates to the newly created profile, ensuring that your changes are actually applied to the settings model's clone in use. ## References microsoft#8773 - Introduced the bug microsoft#6800 - Settings UI Epic ## Validation Steps Performed - Navigate to "powershell" profile - edit "tab title" value - discard changes Before: changes would persist unless you discarded changes again Now: changes are discarded Also verified expected behavior occurs when you click "save" instead of "discard"
Removes the visibility hack in
UpdateSettings
where we were hidingProfile menu items instead of removing them. This hack was removed using
ReplaceAll
. For an unknown reason, callingRemove()
would result inan out-of-bounds error in XAML code.
The "Discard" button would improperly refresh the Settings UI. Both of
the bugs were caused by holding a reference to a hidden menu item then
trying to set the
SelectedItem
to that menu item.Additionally, 9283375 adds a check for the selected item in
SettingsNav_ItemInvoked()
. This prevents navigation to an alreadyselected item. This was the heuristic used by the XAML Controls Gallery.
References #6800 - Settings UI Epic
Validation Steps Performed
(Repeated for each menu item)
Also performed repro steps for #8747 and #8748.
Closes #8747
Closes #8748