Skip to content

Commit

Permalink
Syncing content from committish release/1.4.4
Browse files Browse the repository at this point in the history
  • Loading branch information
reunion-maestro-bot committed Jan 16, 2024
1 parent 685d2bf commit 20dcc18
Show file tree
Hide file tree
Showing 10 changed files with 240 additions and 37 deletions.
1 change: 1 addition & 0 deletions controls/MidlShared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<Midl Remove="@(OriginalMidl)" />
<Midl Include="$(ProjectMergedIdl)">
<MetadataFileName>$(ProjectUnmergedWinmd)</MetadataFileName>
<AdditionalOptions>%(AdditionalOptions) /nomidl</AdditionalOptions>
</Midl>
</ItemGroup>
<MakeDir Directories="$(IntermediateOutputPath)Unmerged" Condition="!Exists('$(IntermediateOutputPath)Unmerged')" />
Expand Down
202 changes: 176 additions & 26 deletions controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
// Bug 47050253: [1.4 servicing] Insiders report that even with the latest bug fixes in the dev channel they can still see the context menu with a transparent background sometimes
#define WINAPPSDK_CHANGEID_47050253 47050253

// Bug 48006563: [1.4 servicing] File Explorer crash due to bad reentrancy from CommandBarFlyoutCommandBar calling FocusCommand (STOWED_EXCEPTION_8000ffff_Microsoft.UI.Xaml.dll!CXcpDispatcher::OnReentrancyProtectedWindowMessage)
#define WINAPPSDK_CHANGEID_48006563 48006563

CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar()
{
SetDefaultStyleKey(this);
Expand Down Expand Up @@ -49,25 +52,50 @@ CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar()
{
if (firstCommandAsFrameworkElement.IsLoaded())
{
FocusCommand(
commands,
usingPrimaryCommands ? m_moreButton.get() : nullptr /*moreButton*/,
winrt::FocusState::Programmatic /*focusState*/,
true /*firstCommand*/,
ensureTabStopUniqueness);
if (WinAppSdk::Containment::IsChangeEnabled<WINAPPSDK_CHANGEID_48006563>())
{
FocusCommandAsync(
commands,
usingPrimaryCommands ? m_moreButton.get() : nullptr /*moreButton*/,
winrt::FocusState::Programmatic /*focusState*/,
true /*firstCommand*/,
ensureTabStopUniqueness);
}
else
{
FocusCommandSync(
commands,
usingPrimaryCommands ? m_moreButton.get() : nullptr /*moreButton*/,
winrt::FocusState::Programmatic /*focusState*/,
true /*firstCommand*/,
ensureTabStopUniqueness);
}
}
else
{
m_firstItemLoadedRevoker = firstCommandAsFrameworkElement.Loaded(winrt::auto_revoke,
{
[this, commands, usingPrimaryCommands, ensureTabStopUniqueness](winrt::IInspectable const& sender, auto const&)
{
FocusCommand(
commands,
usingPrimaryCommands ? m_moreButton.get() : nullptr /*moreButton*/,
winrt::FocusState::Programmatic /*focusState*/,
true /*firstCommand*/,
ensureTabStopUniqueness);
if (WinAppSdk::Containment::IsChangeEnabled<WINAPPSDK_CHANGEID_48006563>())
{
FocusCommandAsync(
commands,
usingPrimaryCommands ? m_moreButton.get() : nullptr /*moreButton*/,
winrt::FocusState::Programmatic /*focusState*/,
true /*firstCommand*/,
ensureTabStopUniqueness);
}
else
{
FocusCommandSync(
commands,
usingPrimaryCommands ? m_moreButton.get() : nullptr /*moreButton*/,
winrt::FocusState::Programmatic /*focusState*/,
true /*firstCommand*/,
ensureTabStopUniqueness);
}

m_firstItemLoadedRevoker.revoke();
}
});
Expand Down Expand Up @@ -1029,12 +1057,24 @@ void CommandBarFlyoutCommandBar::OnKeyDown(
IsOpen(true);

// ... and focus the first focusable command
FocusCommand(
SecondaryCommands() /*commands*/,
nullptr /*moreButton*/,
winrt::FocusState::Keyboard /*focusState*/,
true /*firstCommand*/,
true /*ensureTabStopUniqueness*/);
if (WinAppSdk::Containment::IsChangeEnabled<WINAPPSDK_CHANGEID_48006563>())
{
FocusCommandAsync(
SecondaryCommands() /*commands*/,
nullptr /*moreButton*/,
winrt::FocusState::Keyboard /*focusState*/,
true /*firstCommand*/,
true /*ensureTabStopUniqueness*/);
}
else
{
FocusCommandSync(
SecondaryCommands() /*commands*/,
nullptr /*moreButton*/,
winrt::FocusState::Keyboard /*focusState*/,
true /*firstCommand*/,
true /*ensureTabStopUniqueness*/);
}
}
break;
}
Expand Down Expand Up @@ -1124,15 +1164,29 @@ void CommandBarFlyoutCommandBar::OnKeyDown(
}
}

if (FocusControl(
accessibleControls.GetAt(i) /*newFocus*/,
focusedControl /*oldFocus*/,
winrt::FocusState::Keyboard /*focusState*/,
true /*updateTabStop*/))
if (WinAppSdk::Containment::IsChangeEnabled<WINAPPSDK_CHANGEID_48006563>())
{
FocusControlAsync(
accessibleControls.GetAt(i) /*newFocus*/,
focusedControl /*oldFocus*/,
winrt::FocusState::Keyboard /*focusState*/,
true /*updateTabStop*/);

args.Handled(true);
break;
}
else
{
if (FocusControlSync(
accessibleControls.GetAt(i) /*newFocus*/,
focusedControl /*oldFocus*/,
winrt::FocusState::Keyboard /*focusState*/,
true /*updateTabStop*/))
{
args.Handled(true);
break;
}
}
}
}

Expand Down Expand Up @@ -1174,7 +1228,103 @@ winrt::Control CommandBarFlyoutCommandBar::GetFirstTabStopControl(
return nullptr;
}

bool CommandBarFlyoutCommandBar::FocusControl(
winrt::IAsyncOperation<bool> CommandBarFlyoutCommandBar::FocusControlAsync(
winrt::Control newFocus,
winrt::Control oldFocus,
winrt::FocusState focusState,
bool updateTabStop)
{
MUX_ASSERT(newFocus);

if (updateTabStop)
{
newFocus.IsTabStop(true);
}

// Setting focus can cause us to enter the window message handler loop, which is bad if
// CXcpDispatcher::OnReentrancyProtectedWindowMessage is on the callstack, since that can lead to reentry.
// Switching to a background thread and then back to the UI thread ensures that this call to Control.Focus()
// occurs outside that callstack.
winrt::apartment_context uiThread;
co_await winrt::resume_background();
co_await uiThread;

if (newFocus.Focus(focusState))
{
if (oldFocus && updateTabStop)
{
oldFocus.IsTabStop(false);
}
co_return true;
}
co_return false;
}

winrt::IAsyncOperation<bool> CommandBarFlyoutCommandBar::FocusCommandAsync(
winrt::IObservableVector<winrt::ICommandBarElement> commands,
winrt::Control moreButton,
winrt::FocusState focusState,
bool firstCommand,
bool ensureTabStopUniqueness)
{
COMMANDBARFLYOUT_TRACE_VERBOSE(nullptr, TRACE_MSG_METH, METH_NAME, nullptr);

MUX_ASSERT(commands);

// Give focus to the first or last focusable command
winrt::Control focusedControl = nullptr;
int startIndex = 0;
int endIndex = static_cast<int>(commands.Size());
int deltaIndex = 1;

if (!firstCommand)
{
deltaIndex = -1;
startIndex = endIndex - 1;
endIndex = -1;
}

for (int index = startIndex; index != endIndex; index += deltaIndex)
{
auto command = commands.GetAt(index);

if (auto commandAsControl = command.try_as<winrt::Control>())
{
if (IsControlFocusable(commandAsControl, !ensureTabStopUniqueness /*checkTabStop*/))
{
if (!focusedControl)
{
if (co_await FocusControlAsync(
commandAsControl /*newFocus*/,
nullptr /*oldFocus*/,
focusState /*focusState*/,
ensureTabStopUniqueness /*updateTabStop*/))
{
if (ensureTabStopUniqueness && moreButton && moreButton.IsTabStop())
{
moreButton.IsTabStop(false);
}

focusedControl = commandAsControl;

if (!ensureTabStopUniqueness)
{
break;
}
}
}
else if (focusedControl && commandAsControl.IsTabStop())
{
commandAsControl.IsTabStop(false);
}
}
}
}

co_return focusedControl != nullptr;
}

bool CommandBarFlyoutCommandBar::FocusControlSync(
winrt::Control const& newFocus,
winrt::Control const& oldFocus,
winrt::FocusState const& focusState,
Expand All @@ -1198,7 +1348,7 @@ bool CommandBarFlyoutCommandBar::FocusControl(
return false;
}

bool CommandBarFlyoutCommandBar::FocusCommand(
bool CommandBarFlyoutCommandBar::FocusCommandSync(
winrt::IObservableVector<winrt::ICommandBarElement> const& commands,
winrt::Control const& moreButton,
winrt::FocusState const& focusState,
Expand Down Expand Up @@ -1232,7 +1382,7 @@ bool CommandBarFlyoutCommandBar::FocusCommand(
{
if (!focusedControl)
{
if (FocusControl(
if (FocusControlSync(
commandAsControl /*newFocus*/,
nullptr /*oldFocus*/,
focusState /*focusState*/,
Expand Down
15 changes: 13 additions & 2 deletions controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,23 @@ class CommandBarFlyoutCommandBar :
bool checkTabStop);
static winrt::Control GetFirstTabStopControl(
winrt::IObservableVector<winrt::ICommandBarElement> const& commands);
static bool FocusControl(
static winrt::IAsyncOperation<bool> FocusControlAsync(
winrt::Control newFocus,
winrt::Control oldFocus,
winrt::FocusState focusState,
bool updateTabStop);
static winrt::IAsyncOperation<bool> FocusCommandAsync(
winrt::IObservableVector<winrt::ICommandBarElement> commands,
winrt::Control moreButton,
winrt::FocusState focusState,
bool firstCommand,
bool ensureTabStopUniqueness);
static bool FocusControlSync(
winrt::Control const& newFocus,
winrt::Control const& oldFocus,
winrt::FocusState const& focusState,
bool updateTabStop);
static bool FocusCommand(
static bool FocusCommandSync(
winrt::IObservableVector<winrt::ICommandBarElement> const& commands,
winrt::Control const& moreButton,
winrt::FocusState const& focusState,
Expand Down
17 changes: 17 additions & 0 deletions controls/dev/CommandBarFlyout/TestUI/CommandBarFlyoutPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,22 @@
<AppBarButton Label="Text" />
</muxc:CommandBarFlyout.SecondaryCommands>
</muxc:CommandBarFlyout>
<muxc:CommandBarFlyout Placement="Right" x:Name="Flyout12" AutomationProperties.AutomationId="Flyout12" Opened="OnFlyoutOpened" Closed="OnFlyoutClosed">
<AppBarButton x:Name="CutButton12" AutomationProperties.AutomationId="CutButton12" Label="Cut" Icon="Cut" Click="OnElementClicked"/>
<AppBarSeparator />
<AppBarButton x:Name="CopyButton12" AutomationProperties.AutomationId="CopyButton12" Label="Copy" Icon="Copy" Click="OnElementClicked" />
<AppBarButton x:Name="PasteButton12" AutomationProperties.AutomationId="PasteButton12" Label="Paste" Icon="Paste" Click="OnElementClicked" />
<AppBarButton x:Name="BoldButton12" AutomationProperties.AutomationId="BoldButton12" Label="Bold" Icon="Bold" Click="OnElementClicked" />
<AppBarButton x:Name="ItalicButton12" AutomationProperties.AutomationId="ItalicButton12" Label="Italic" Icon="Italic" Click="OnElementClicked" />
<AppBarToggleButton x:Name="UnderlineButton12" AutomationProperties.AutomationId="UnderlineButton12" Label="Underline" Icon="Underline" Click="OnElementClicked" />
<muxc:CommandBarFlyout.SecondaryCommands>
<AppBarButton x:Name="UndoButton12" AutomationProperties.AutomationId="UndoButton12" Label="Undo" Icon="Undo" Click="OnElementClicked" />
<AppBarSeparator IsTabStop="False" />
<AppBarButton x:Name="RedoButton12" AutomationProperties.AutomationId="RedoButton12" Label="Redo" Icon="Redo" Click="OnElementClicked" />
<AppBarButton x:Name="SelectAllButton12" AutomationProperties.AutomationId="SelectAllButton12" Label="Select all" Click="OnElementClicked" />
<AppBarToggleButton x:Name="FavoriteToggleButton12" AutomationProperties.AutomationId="FavoriteToggleButton12" Label="Favorite" Icon="Favorite" Checked="OnElementChecked" Unchecked="OnElementUnchecked" />
</muxc:CommandBarFlyout.SecondaryCommands>
</muxc:CommandBarFlyout>
</Grid.Resources>
<ScrollViewer>
<StackPanel>
Expand All @@ -247,6 +263,7 @@
<Button x:Name="FlyoutTarget9" Content="Show CommandBarFlyout with AlwaysExpanded" Margin="10" Click="OnFlyoutTarget9Click" />
<Button x:Name="FlyoutTarget10" Content="Show Windows.UI.Xaml.Controls.CommandBarFlyout" Margin="10" Click="OnFlyoutTarget10Click" />
<Button x:Name="FlyoutTarget11" Content="Show CommandBarFlyout with potential layout cycle at 125% scale" Margin="10" Click="OnFlyoutTarget11Click" />
<Button x:Name="FlyoutTarget12" Content="Show CommandBarFlyout after a five-second delay" Margin="10" Click="OnFlyoutTarget12Click" />
<CheckBox x:Name="IsRTLCheckBox" Content="Is Page in RightToLeft FlowDirection?" AutomationProperties.AutomationId="IsRTLCheckBox" Margin="10,10,10,2" Checked="IsRTLCheckBox_Checked" Unchecked="IsRTLCheckBox_Unchecked" />
<StackPanel Orientation="Horizontal" Margin="10,0,10,2">
<CheckBox x:Name="AddPrimaryCommandDynamicallyCheckBox" Content="Add Primary Commands Dynamically?" AutomationProperties.AutomationId="AddPrimaryCommandDynamicallyCheckBox" />
Expand Down
Loading

0 comments on commit 20dcc18

Please sign in to comment.