diff --git a/samples/v1.5/Test/PrimarySecondaryShowCards.json b/samples/v1.5/Test/PrimarySecondaryShowCards.json new file mode 100644 index 0000000000..6f24a53f39 --- /dev/null +++ b/samples/v1.5/Test/PrimarySecondaryShowCards.json @@ -0,0 +1,36 @@ +{ + "type": "AdaptiveCard", + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "version": "1.5", + "actions": [ + { + "type": "Action.ShowCard", + "title": "Primary Show Card Action", + "card": { + "type": "AdaptiveCard", + "body": [ + { + "type": "TextBlock", + "text": "Primary Show Card", + "wrap": true + } + ] + } + }, + { + "type": "Action.ShowCard", + "title": "Secondary Show Card Action", + "card": { + "type": "AdaptiveCard", + "body": [ + { + "type": "TextBlock", + "text": "Secondary Show Card", + "wrap": true + } + ] + }, + "mode": "secondary" + } + ] +} diff --git a/source/uwp/Renderer/lib/ActionHelpers.cpp b/source/uwp/Renderer/lib/ActionHelpers.cpp index c33123d0a0..b4a28f83f6 100644 --- a/source/uwp/Renderer/lib/ActionHelpers.cpp +++ b/source/uwp/Renderer/lib/ActionHelpers.cpp @@ -453,10 +453,11 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers ComPtr actionInvoker; RETURN_IF_FAILED(renderContext->get_ActionInvoker(&actionInvoker)); EventRegistrationToken clickToken; - RETURN_IF_FAILED(buttonBase->add_Click(Callback([action, actionInvoker](IInspectable* /*sender*/, IRoutedEventArgs* - /*args*/) -> HRESULT { - return actionInvoker->SendActionEvent(action.Get()); - }).Get(), + RETURN_IF_FAILED(buttonBase->add_Click(Callback( + [action, actionInvoker](IInspectable* /*sender*/, IRoutedEventArgs* + /*args*/) -> HRESULT + { return actionInvoker->SendActionEvent(action.Get()); }) + .Get(), &clickToken)); boolean isEnabled; @@ -648,13 +649,17 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers // Make the action the same size as the text box EventRegistrationToken eventToken; THROW_IF_FAILED(textBoxContainerAsFrameworkElement->add_Loaded( - Callback([actionUIElement, textBoxContainerAsFrameworkElement](IInspectable* /*sender*/, IRoutedEventArgs* - /*args*/) -> HRESULT { - ComPtr actionFrameworkElement; - RETURN_IF_FAILED(actionUIElement.As(&actionFrameworkElement)); + Callback( + [actionUIElement, textBoxContainerAsFrameworkElement](IInspectable* /*sender*/, IRoutedEventArgs* + /*args*/) -> HRESULT + { + ComPtr actionFrameworkElement; + RETURN_IF_FAILED(actionUIElement.As(&actionFrameworkElement)); - return ActionHelpers::SetMatchingHeight(actionFrameworkElement.Get(), textBoxContainerAsFrameworkElement.Get()); - }).Get(), + return ActionHelpers::SetMatchingHeight(actionFrameworkElement.Get(), + textBoxContainerAsFrameworkElement.Get()); + }) + .Get(), &eventToken)); // Wrap the action in a button @@ -694,9 +699,10 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers EventRegistrationToken keyDownEventToken; THROW_IF_FAILED(textBoxAsUIElement->add_KeyDown( - Callback([actionInvoker, localInlineAction](IInspectable* /*sender*/, IKeyRoutedEventArgs* args) -> HRESULT { - return HandleKeydownForInlineAction(args, actionInvoker.Get(), localInlineAction.Get()); - }).Get(), + Callback( + [actionInvoker, localInlineAction](IInspectable* /*sender*/, IKeyRoutedEventArgs* args) -> HRESULT + { return HandleKeydownForInlineAction(args, actionInvoker.Get(), localInlineAction.Get()); }) + .Get(), &keyDownEventToken)); } @@ -841,11 +847,14 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers THROW_IF_FAILED(localButton.As(&buttonBase)); EventRegistrationToken clickToken; - THROW_IF_FAILED(buttonBase->add_Click(Callback([strongAction, actionInvoker](IInspectable* /*sender*/, IRoutedEventArgs* - /*args*/) -> HRESULT { - THROW_IF_FAILED(actionInvoker->SendActionEvent(strongAction.Get())); - return S_OK; - }).Get(), + THROW_IF_FAILED(buttonBase->add_Click(Callback( + [strongAction, actionInvoker](IInspectable* /*sender*/, IRoutedEventArgs* + /*args*/) -> HRESULT + { + THROW_IF_FAILED(actionInvoker->SendActionEvent(strongAction.Get())); + return S_OK; + }) + .Get(), &clickToken)); } @@ -937,9 +946,68 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers return S_OK; } + HRESULT BuildInlineShowCard(_In_opt_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveCard* adaptiveCard, + _In_opt_ IAdaptiveActionSet* adaptiveActionSet, + _In_ IAdaptiveActionElement* showCardAction, + _In_ IPanel* showCardsPanel, + _In_ IAdaptiveRenderContext* renderContext, + _In_ IAdaptiveRenderArgs* renderArgs) + { + ComPtr action(showCardAction); + + // Check the host config to see if we need to build inline show cards + ComPtr hostConfig; + RETURN_IF_FAILED(renderContext->get_HostConfig(&hostConfig)); + ComPtr actionsConfig; + RETURN_IF_FAILED(hostConfig->get_Actions(actionsConfig.GetAddressOf())); + + ComPtr showCardActionConfig; + RETURN_IF_FAILED(actionsConfig->get_ShowCard(&showCardActionConfig)); + + ABI::AdaptiveCards::Rendering::Uwp::ActionMode showCardActionMode; + RETURN_IF_FAILED(showCardActionConfig->get_ActionMode(&showCardActionMode)); + + if (showCardActionMode == ABI::AdaptiveCards::Rendering::Uwp::ActionMode::Inline) + { + // Get the card to be shown + ComPtr actionAsShowCardAction; + RETURN_IF_FAILED(action.As(&actionAsShowCardAction)); + + ComPtr showCard; + RETURN_IF_FAILED(actionAsShowCardAction->get_Card(&showCard)); + + // Render the card and add it to the show card panel + ComPtr uiShowCard; + RETURN_IF_FAILED(AdaptiveShowCardActionRenderer::BuildShowCard( + showCard.Get(), renderContext, renderArgs, (adaptiveActionSet == nullptr), uiShowCard.GetAddressOf())); + + XamlHelpers::AppendXamlElementToPanel(uiShowCard.Get(), showCardsPanel); + + // Register the show card with the render context + ComPtr contextImpl = + PeekInnards(renderContext); + + if (adaptiveActionSet) + { + RETURN_IF_FAILED( + contextImpl->AddInlineShowCard(adaptiveActionSet, actionAsShowCardAction.Get(), uiShowCard.Get(), renderArgs)); + } + else + { + RETURN_IF_FAILED(contextImpl->AddInlineShowCard(adaptiveCard, actionAsShowCardAction.Get(), uiShowCard.Get(), renderArgs)); + } + } + + return S_OK; + } + HRESULT AddOverflowFlyoutItem(_In_ IAdaptiveActionElement* action, _In_ IButton* overflowButton, + _In_opt_ IAdaptiveCard* adaptiveCard, + _In_opt_ IAdaptiveActionSet* adaptiveActionSet, + _In_ IPanel* showCardPanel, _In_ IAdaptiveRenderContext* renderContext, + _In_ IAdaptiveRenderArgs* renderArgs, _COM_Outptr_ IUIElement** flyoutUIItem) { // Get the flyout items vector @@ -978,12 +1046,21 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers ComPtr actionInvoker; RETURN_IF_FAILED(renderContext->get_ActionInvoker(&actionInvoker)); EventRegistrationToken clickToken; - RETURN_IF_FAILED(flyoutItem->add_Click(Callback([actionParam, actionInvoker](IInspectable* /*sender*/, IRoutedEventArgs* - /*args*/) -> HRESULT { - return actionInvoker->SendActionEvent(actionParam.Get()); - }).Get(), + RETURN_IF_FAILED(flyoutItem->add_Click(Callback( + [actionParam, actionInvoker](IInspectable* /*sender*/, IRoutedEventArgs* + /*args*/) -> HRESULT + { return actionInvoker->SendActionEvent(actionParam.Get()); }) + .Get(), &clickToken)); + ABI::AdaptiveCards::ObjectModel::Uwp::ActionType actionType; + RETURN_IF_FAILED(action->get_ActionType(&actionType)); + if (actionType == ABI::AdaptiveCards::ObjectModel::Uwp::ActionType::ShowCard) + { + // Build the inline show card. + RETURN_IF_FAILED(BuildInlineShowCard(adaptiveCard, adaptiveActionSet, action, showCardPanel, renderContext, renderArgs)); + } + // Add the new menu item to the vector ComPtr flyoutItemAsBase; RETURN_IF_FAILED(flyoutItem.As(&flyoutItemAsBase)); @@ -996,74 +1073,13 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers return S_OK; } - HRESULT BuildInlineShowCard(_In_opt_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveCard* adaptiveCard, - _In_opt_ IAdaptiveActionSet* adaptiveActionSet, - _In_ IAdaptiveActionElement* showCardAction, - _In_ IPanel* showCardsPanel, - _In_ IUIElement* actionButton, - _In_ IUIElement* actionOverflowItem, - UINT buttonIndex, - _In_ IAdaptiveRenderContext* renderContext, - _In_ IAdaptiveRenderArgs* renderArgs) - { - ComPtr action(showCardAction); - - // Check the host config to see if we need to build inline show cards - ComPtr hostConfig; - RETURN_IF_FAILED(renderContext->get_HostConfig(&hostConfig)); - ComPtr actionsConfig; - RETURN_IF_FAILED(hostConfig->get_Actions(actionsConfig.GetAddressOf())); - - ComPtr showCardActionConfig; - RETURN_IF_FAILED(actionsConfig->get_ShowCard(&showCardActionConfig)); - - ABI::AdaptiveCards::Rendering::Uwp::ActionMode showCardActionMode; - RETURN_IF_FAILED(showCardActionConfig->get_ActionMode(&showCardActionMode)); - - if (showCardActionMode == ABI::AdaptiveCards::Rendering::Uwp::ActionMode::Inline) - { - // Get the card to be shown - ComPtr actionAsShowCardAction; - RETURN_IF_FAILED(action.As(&actionAsShowCardAction)); - - ComPtr showCard; - RETURN_IF_FAILED(actionAsShowCardAction->get_Card(&showCard)); - - // Render the card and add it to the show card panel - ComPtr uiShowCard; - RETURN_IF_FAILED(AdaptiveShowCardActionRenderer::BuildShowCard( - showCard.Get(), renderContext, renderArgs, (adaptiveActionSet == nullptr), uiShowCard.GetAddressOf())); - - XamlHelpers::AppendXamlElementToPanel(uiShowCard.Get(), showCardsPanel); - - // Register the show card with the render context - ComPtr contextImpl = - PeekInnards(renderContext); - - if (adaptiveActionSet) - { - RETURN_IF_FAILED(contextImpl->AddInlineShowCard( - adaptiveActionSet, actionAsShowCardAction.Get(), actionButton, actionOverflowItem, uiShowCard.Get(), buttonIndex, renderArgs)); - } - else - { - RETURN_IF_FAILED(contextImpl->AddInlineShowCard( - adaptiveCard, actionAsShowCardAction.Get(), actionButton, actionOverflowItem, uiShowCard.Get(), buttonIndex, renderArgs)); - } - } - - return S_OK; - } - HRESULT CreateActionButtonInActionSet(_In_opt_ IAdaptiveCard* adaptiveCard, _In_opt_ IAdaptiveActionSet* adaptiveActionSet, _In_ IAdaptiveActionElement* actionToCreate, - bool buttonVisible, UINT columnIndex, _In_ IPanel* actionsPanel, _In_ IPanel* showCardPanel, _In_ IVector* columnDefinitions, - _In_opt_ IUIElement* overflowItem, _In_ IAdaptiveRenderContext* renderContext, _In_ IAdaptiveRenderArgs* renderArgs, _COM_Outptr_ IUIElement** returnedActionControl) @@ -1120,11 +1136,6 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers RETURN_IF_FAILED(renderer->Render(action.Get(), renderContext, renderArgs, &actionControl)); - if (!buttonVisible) - { - RETURN_IF_FAILED(actionControl->put_Visibility(Visibility_Collapsed)); - } - XamlHelpers::AppendXamlElementToPanel(actionControl.Get(), actionsPanel); if (columnDefinitions != nullptr) @@ -1134,14 +1145,7 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers HStringReference(RuntimeClass_Windows_UI_Xaml_Controls_ColumnDefinition)); RETURN_IF_FAILED(columnDefinitions->Append(columnDefinition.Get())); - if (buttonVisible) - { - RETURN_IF_FAILED(columnDefinition->put_Width({1.0, GridUnitType::GridUnitType_Star})); - } - else - { - RETURN_IF_FAILED(columnDefinition->put_Width({0, GridUnitType::GridUnitType_Auto})); - } + RETURN_IF_FAILED(columnDefinition->put_Width({0, GridUnitType::GridUnitType_Auto})); ComPtr gridStatics; RETURN_IF_FAILED(GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Xaml_Controls_Grid).Get(), &gridStatics)); @@ -1154,8 +1158,7 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers if (actionType == ABI::AdaptiveCards::ObjectModel::Uwp::ActionType::ShowCard) { // Build the inline show card. - RETURN_IF_FAILED(BuildInlineShowCard( - adaptiveCard, adaptiveActionSet, action.Get(), showCardPanel, actionControl.Get(), overflowItem, columnIndex, renderContext, renderArgs)); + RETURN_IF_FAILED(BuildInlineShowCard(adaptiveCard, adaptiveActionSet, action.Get(), showCardPanel, renderContext, renderArgs)); } RETURN_IF_FAILED(actionControl.CopyTo(returnedActionControl)); @@ -1261,33 +1264,20 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers boolean overflowMaxActions; RETURN_IF_FAILED(hostConfigImpl->get_OverflowMaxActions(&overflowMaxActions)); - UINT currentButtonIndex = 0; - UINT lastPrimaryActionIndex = 0; - ComPtr lastPrimaryAction; bool allActionsHaveIcons{true}; - IterateOverVector(children, [&](IAdaptiveActionElement* child) { - HString iconUrl; - RETURN_IF_FAILED(child->get_IconUrl(iconUrl.GetAddressOf())); + IterateOverVector(children, + [&](IAdaptiveActionElement* child) + { + HString iconUrl; + RETURN_IF_FAILED(child->get_IconUrl(iconUrl.GetAddressOf())); - if (WindowsIsStringEmpty(iconUrl.Get())) - { - allActionsHaveIcons = false; - } + if (WindowsIsStringEmpty(iconUrl.Get())) + { + allActionsHaveIcons = false; + } - // We need to figure out which button is going to be the last visible button. That button may need to be - // handled separately if we have show card actions in the overflow menu. - ComPtr action(child); - ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode mode; - RETURN_IF_FAILED(action->get_Mode(&mode)); - if (currentButtonIndex < maxActions && mode == ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode::Primary) - { - lastPrimaryActionIndex = currentButtonIndex; - lastPrimaryAction = action; - currentButtonIndex++; - } - - return S_OK; - }); + return S_OK; + }); RETURN_IF_FAILED(renderArgs->put_AllowAboveTitleIconPlacement(allActionsHaveIcons)); @@ -1299,135 +1289,69 @@ namespace AdaptiveCards::Rendering::Uwp::ActionHelpers ComPtr gridStatics; RETURN_IF_FAILED(GetActivationFactory(HStringReference(RuntimeClass_Windows_UI_Xaml_Controls_Grid).Get(), &gridStatics)); - currentButtonIndex = 0; - bool pastLastPrimaryAction = false; + UINT currentButtonIndex = 0; ComPtr overflowButton; - std::vector, ComPtr>> showCardOverflowButtons; - IterateOverVector(children, [&](IAdaptiveActionElement* child) { - ComPtr action(child); - ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode mode; - RETURN_IF_FAILED(action->get_Mode(&mode)); + IterateOverVector( + children, + [&](IAdaptiveActionElement* child) + { + ComPtr action(child); + ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode mode; + RETURN_IF_FAILED(action->get_Mode(&mode)); - ABI::AdaptiveCards::ObjectModel::Uwp::ActionType actionType; - RETURN_IF_FAILED(action->get_ActionType(&actionType)); + ABI::AdaptiveCards::ObjectModel::Uwp::ActionType actionType; + RETURN_IF_FAILED(action->get_ActionType(&actionType)); - ComPtr actionControl; - if (!pastLastPrimaryAction && mode == ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode::Primary) - { - // The last button may need special handling so don't handle it here - if (currentButtonIndex != lastPrimaryActionIndex) + ComPtr actionControl; + if (currentButtonIndex < maxActions && mode == ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode::Primary) { // If we have fewer than the maximum number of actions and this action's mode is primary, make a button RETURN_IF_FAILED(CreateActionButtonInActionSet(adaptiveCard, adaptiveActionSet, action.Get(), - true, currentButtonIndex, actionsPanel.Get(), showCardsPanel.Get(), columnDefinitions.Get(), - nullptr, renderContext, renderArgs, &actionControl)); currentButtonIndex++; } - else - { - pastLastPrimaryAction = true; - } - } - else if (pastLastPrimaryAction && (mode == ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode::Primary) && !overflowMaxActions) - { - // If we have more primary actions than the max actions and we're not allowed to overflow them just set a warning and continue - renderContext->AddWarning(ABI::AdaptiveCards::ObjectModel::Uwp::WarningStatusCode::MaxActionsExceeded, - HStringReference(L"Some actions were not rendered due to exceeding the maximum number of actions allowed") - .Get()); - return S_OK; - } - else - { - // If the action's mode is secondary or we're overflowing max actions, create a flyout item on the overflow menu - if (overflowButton == nullptr) - { - // Create a button for the overflow menu if it doesn't exist yet - RETURN_IF_FAILED(CreateFlyoutButton(renderContext, renderArgs, &overflowButton)); - } - - // Add a flyout item to the overflow menu - RETURN_IF_FAILED(AddOverflowFlyoutItem(action.Get(), overflowButton.Get(), renderContext, &actionControl)); - - // Show card actions in the overflow menu move to the action bar when selected, so we need to keep track - // of these so we can make non-visible buttons for them later. They need to go after all the actions we're creating as primary. - if (actionType == ActionType_ShowCard) - { - showCardOverflowButtons.emplace_back(std::make_pair(action, actionControl)); - } - - // If this was supposed to be a primary action but it got overflowed due to max actions, add a warning - if (mode == ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode::Primary) + else if (currentButtonIndex >= maxActions && + (mode == ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode::Primary) && !overflowMaxActions) { + // If we have more primary actions than the max actions and we're not allowed to overflow them just set a warning and continue renderContext->AddWarning(ABI::AdaptiveCards::ObjectModel::Uwp::WarningStatusCode::MaxActionsExceeded, - HStringReference(L"Some actions were moved to an overflow menu due to exceeding the maximum number of actions allowed") + HStringReference(L"Some actions were not rendered due to exceeding the maximum number of actions allowed") .Get()); + return S_OK; + } + else + { + // If the action's mode is secondary or we're overflowing max actions, create a flyout item on the overflow menu + if (overflowButton == nullptr) + { + // Create a button for the overflow menu if it doesn't exist yet + RETURN_IF_FAILED(CreateFlyoutButton(renderContext, renderArgs, &overflowButton)); + } + + // Add a flyout item to the overflow menu + RETURN_IF_FAILED(AddOverflowFlyoutItem( + action.Get(), overflowButton.Get(), adaptiveCard, adaptiveActionSet, showCardsPanel.Get(), renderContext, renderArgs, &actionControl)); + + // If this was supposed to be a primary action but it got overflowed due to max actions, add a warning + if (mode == ABI::AdaptiveCards::ObjectModel::Uwp::ActionMode::Primary) + { + renderContext->AddWarning(ABI::AdaptiveCards::ObjectModel::Uwp::WarningStatusCode::MaxActionsExceeded, + HStringReference(L"Some actions were moved to an overflow menu due to exceeding the maximum number of actions allowed") + .Get()); + } } - } - - return S_OK; - }); - - ComPtr lastActionOverflowItem; - if (overflowButton != nullptr && showCardOverflowButtons.size() != 0) - { - // Show card actions from the overflow menu switch places with the last primary action when invoked. - // If we any primary actions, create a non-visible overflow item for the last primary action. This - // allows it to be moved to the overflow menu to make space for a show card action. - - if (lastPrimaryAction != nullptr) - { - RETURN_IF_FAILED(AddOverflowFlyoutItem(lastPrimaryAction.Get(), overflowButton.Get(), renderContext, &lastActionOverflowItem)); - RETURN_IF_FAILED(lastActionOverflowItem->put_Visibility(Visibility_Collapsed)); - } - - // Create non-visible primary buttons for all overflow show card actions so they can be moved to the action bar when invoked - for (auto& overflowShowCard : showCardOverflowButtons) - { - ComPtr createdButton; - RETURN_IF_FAILED(CreateActionButtonInActionSet(adaptiveCard, - adaptiveActionSet, - overflowShowCard.first.Get(), - false, - currentButtonIndex, - actionsPanel.Get(), - showCardsPanel.Get(), - columnDefinitions.Get(), - overflowShowCard.second.Get(), - renderContext, - renderArgs, - &createdButton)); - currentButtonIndex++; - } - } - // Now that we have the overflow item if needed, we can create the button for the last primary action - if (lastPrimaryAction != nullptr) - { - ComPtr createdButton; - RETURN_IF_FAILED(CreateActionButtonInActionSet(adaptiveCard, - adaptiveActionSet, - lastPrimaryAction.Get(), - true, - currentButtonIndex, - actionsPanel.Get(), - showCardsPanel.Get(), - columnDefinitions.Get(), - lastActionOverflowItem.Get(), - renderContext, - renderArgs, - &createdButton)); - currentButtonIndex++; - } + return S_OK; + }); // Lastly add the overflow button itself to the action panel if (overflowButton != nullptr) diff --git a/source/uwp/Renderer/lib/AdaptiveRenderContext.cpp b/source/uwp/Renderer/lib/AdaptiveRenderContext.cpp index 8da7c03865..1a8f7d749d 100644 --- a/source/uwp/Renderer/lib/AdaptiveRenderContext.cpp +++ b/source/uwp/Renderer/lib/AdaptiveRenderContext.cpp @@ -128,30 +128,24 @@ namespace AdaptiveCards::Rendering::Uwp HRESULT AdaptiveRenderContext::AddInlineShowCard(ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveCard* adaptiveCard, ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs) { ComPtr renderResult; RETURN_IF_FAILED(GetRenderResult(renderResult.GetAddressOf())); return renderResult->AddInlineShowCard( - adaptiveCard, showCardAction, actionButtonUIElement, actionOverflowUIElement, showCardUIElement, primaryButtonIndex, renderArgs); + adaptiveCard, showCardAction, showCardUIElement, renderArgs); } HRESULT AdaptiveRenderContext::AddInlineShowCard(_In_opt_ IAdaptiveActionSet* actionSet, _In_ IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, _In_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs) { ComPtr renderResult; RETURN_IF_FAILED(GetRenderResult(renderResult.GetAddressOf())); return renderResult->AddInlineShowCard( - actionSet, showCardAction, actionButtonUIElement, actionOverflowUIElement, showCardUIElement, primaryButtonIndex, renderArgs); + actionSet, showCardAction, showCardUIElement, renderArgs); } HRESULT AdaptiveRenderContext::AddOverflowButton(_In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveActionSet* actionSet, diff --git a/source/uwp/Renderer/lib/AdaptiveRenderContext.h b/source/uwp/Renderer/lib/AdaptiveRenderContext.h index d62fc45949..ef43430e86 100644 --- a/source/uwp/Renderer/lib/AdaptiveRenderContext.h +++ b/source/uwp/Renderer/lib/AdaptiveRenderContext.h @@ -64,18 +64,12 @@ namespace AdaptiveCards::Rendering::Uwp HRESULT AddInlineShowCard(_In_opt_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveActionSet* actionSet, _In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, _In_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs); HRESULT AddInlineShowCard(_In_opt_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveCard* adaptiveCard, _In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, _In_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs); HRESULT AddOverflowButton(_In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveActionSet* actionSet, diff --git a/source/uwp/Renderer/lib/RenderedAdaptiveCard.cpp b/source/uwp/Renderer/lib/RenderedAdaptiveCard.cpp index 8609a16dc0..41a0886ff9 100644 --- a/source/uwp/Renderer/lib/RenderedAdaptiveCard.cpp +++ b/source/uwp/Renderer/lib/RenderedAdaptiveCard.cpp @@ -116,128 +116,6 @@ namespace AdaptiveCards::Rendering::Uwp { std::shared_ptr showCardInfoToHandle = found->second; - Visibility overflowButtonVisibility = Visibility_Collapsed; - if (showCardInfoToHandle->overflowUIElement != nullptr) - { - RETURN_IF_FAILED(showCardInfoToHandle->overflowUIElement->get_Visibility(&overflowButtonVisibility)); - } - - // Check if the action is being invoked from the overflow menu - if (overflowButtonVisibility == Visibility_Visible) - { - // When a show card action is selected from the overflow menu, we need to move it from the overflow menu - // to the action bar by swapping it with the last item currently there. In order to do this we make this - // action non-visible in the flyout menu and visible in the action bar, and we make the last action - // visible in the action bar visible in the flyout menu and non-visible in the action bar. - - auto overflowButtonPair = m_overflowButtons.find(showCardInfoToHandle->actionSetId); - ComPtr overflowButton = overflowButtonPair->second; - - ComPtr overflowButtonAsFrameworkElement; - RETURN_IF_FAILED(overflowButton.As(&overflowButtonAsFrameworkElement)); - - ComPtr buttonParent; - RETURN_IF_FAILED(overflowButtonAsFrameworkElement->get_Parent(&buttonParent)); - - ComPtr actionPanel; - RETURN_IF_FAILED(buttonParent.As(&actionPanel)); - - ComPtr> actionButtons; - RETURN_IF_FAILED(actionPanel->get_Children(&actionButtons)); - - // In some cases the action panel will be a grid with column definitions that will need to be updated so get those now - ComPtr actionPanelAsGrid; - buttonParent.As(&actionPanelAsGrid); - - ComPtr> columnDefinitions; - if (actionPanelAsGrid != nullptr) - { - RETURN_IF_FAILED(actionPanelAsGrid->get_ColumnDefinitions(&columnDefinitions)); - } - - // Walk the buttons in the button bar. We're looking for the last visible action button (so we can hide - // it now that it's visible on the overflow menu). This will be the second to last visible button in the - // panel (the last being the overflow button itself) - UINT currentButtonIndex = 0; - UINT lastVisibleButtonIndex = 0; - UINT penultimateVisibleButtonIndex = 0; - ComPtr lastVisibleButton; - ComPtr penultimateVisibleButton; - IterateOverVector(actionButtons.Get(), [&](IUIElement* actionUIElement) { - ComPtr action(actionUIElement); - - Visibility visibility; - RETURN_IF_FAILED(action->get_Visibility(&visibility)); - - if (visibility == Visibility_Visible) - { - // Keep track of the second to last visible button to collapse (and it's index to update column definitions) - penultimateVisibleButton = lastVisibleButton; - lastVisibleButton = action; - - penultimateVisibleButtonIndex = lastVisibleButtonIndex; - lastVisibleButtonIndex = currentButtonIndex; - } - - currentButtonIndex++; - return S_OK; - }); - - // If there isn't a visible button available to swap this show card with, there's nothing to do here. - if (penultimateVisibleButton != nullptr) - { - // Hide the last visible non-overflow button (that action will be shown in the overflow menu) - RETURN_IF_FAILED(penultimateVisibleButton->put_Visibility(Visibility_Collapsed)); - - // Set the column width to auto if we're using column definitions to allow the space allocated for this button to collapse - if (columnDefinitions != nullptr) - { - ComPtr columnDefinition; - RETURN_IF_FAILED(columnDefinitions->GetAt(penultimateVisibleButtonIndex, &columnDefinition)); - RETURN_IF_FAILED(columnDefinition->put_Width({0, GridUnitType::GridUnitType_Auto})); - } - - // Make the show card button visible - RETURN_IF_FAILED(showCardInfoToHandle->buttonUIElement->put_Visibility(Visibility_Visible)); - - // Set the column width to 1* if we're using column definitions to show equal width buttons - if (columnDefinitions != nullptr) - { - ComPtr columnDefinition; - RETURN_IF_FAILED(columnDefinitions->GetAt(showCardInfoToHandle->primaryButtonIndex, &columnDefinition)); - RETURN_IF_FAILED(columnDefinition->put_Width({1.0, GridUnitType::GridUnitType_Star})); - } - - // Next get the flyout menu so we can collapse this action from the flyout and show the one we hid from the action bar - ComPtr overflowButtonAsButtonWithFlyout; - RETURN_IF_FAILED(overflowButton.As(&overflowButtonAsButtonWithFlyout)); - - ComPtr flyoutBase; - RETURN_IF_FAILED(overflowButtonAsButtonWithFlyout->get_Flyout(&flyoutBase)); - - ComPtr flyout; - RETURN_IF_FAILED(flyoutBase.As(&flyout)); - - // Get the menu flyout items - ComPtr> flyoutItems; - RETURN_IF_FAILED(flyout->get_Items(&flyoutItems)); - - // Make all items visible to ensure the action we removed from the button panel shows up in the overflow menu - IterateOverVector(flyoutItems.Get(), [&](IMenuFlyoutItemBase* flyoutItem) { - ComPtr flyoutItemBase(flyoutItem); - - ComPtr flyoutItemAsUIElement; - RETURN_IF_FAILED(flyoutItemBase.As(&flyoutItemAsUIElement)); - RETURN_IF_FAILED(flyoutItemAsUIElement->put_Visibility(Visibility_Visible)); - - return S_OK; - }); - - // Make the the action we're handling collapsed in the overflow menu. It is now shown in the button bar and don't want it to show here. - RETURN_IF_FAILED(showCardInfoToHandle->overflowUIElement->put_Visibility(Visibility_Collapsed)); - } - } - // Determine if the card is currently being shown ABI::Windows::UI::Xaml::Visibility currentVisibility; showCardInfoToHandle->cardUIElement->get_Visibility(¤tVisibility); @@ -503,10 +381,7 @@ namespace AdaptiveCards::Rendering::Uwp HRESULT RenderedAdaptiveCard::AddInlineShowCard(_In_ IAdaptiveActionSet* actionSet, _In_ IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs) try { @@ -514,7 +389,7 @@ namespace AdaptiveCards::Rendering::Uwp RETURN_IF_FAILED(actionSet->get_InternalId(&actionSetId)); RETURN_IF_FAILED(AddInlineShowCardHelper( - actionSetId, showCardAction, actionButtonUIElement, actionOverflowUIElement, showCardUIElement, primaryButtonIndex, renderArgs)); + actionSetId, showCardAction, showCardUIElement, renderArgs)); return S_OK; } @@ -522,18 +397,14 @@ namespace AdaptiveCards::Rendering::Uwp HRESULT RenderedAdaptiveCard::AddInlineShowCard(ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveCard* adaptiveCard, ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, _In_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs) try { UINT32 actionSetId; RETURN_IF_FAILED(adaptiveCard->get_InternalId(&actionSetId)); - RETURN_IF_FAILED(AddInlineShowCardHelper( - actionSetId, showCardAction, actionButtonUIElement, actionOverflowUIElement, showCardUIElement, primaryButtonIndex, renderArgs)); + RETURN_IF_FAILED(AddInlineShowCardHelper(actionSetId, showCardAction, showCardUIElement, renderArgs)); return S_OK; } @@ -541,10 +412,7 @@ namespace AdaptiveCards::Rendering::Uwp HRESULT RenderedAdaptiveCard::AddInlineShowCardHelper(UINT32 actionSetId, _In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, _In_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs) try { @@ -553,10 +421,7 @@ namespace AdaptiveCards::Rendering::Uwp std::shared_ptr showCardInfo = std::make_shared(); showCardInfo->actionSetId = actionSetId; - showCardInfo->buttonUIElement = actionButtonUIElement; - showCardInfo->overflowUIElement = actionOverflowUIElement; showCardInfo->cardUIElement = showCardUIElement; - showCardInfo->primaryButtonIndex = primaryButtonIndex; m_showCards.emplace(std::make_pair(showCardActionId, showCardInfo)); @@ -637,5 +502,4 @@ namespace AdaptiveCards::Rendering::Uwp { return m_inputs->GetInputValue(inputElement, inputValue); } - } diff --git a/source/uwp/Renderer/lib/RenderedAdaptiveCard.h b/source/uwp/Renderer/lib/RenderedAdaptiveCard.h index 17295d8d73..cb0b483794 100644 --- a/source/uwp/Renderer/lib/RenderedAdaptiveCard.h +++ b/source/uwp/Renderer/lib/RenderedAdaptiveCard.h @@ -52,18 +52,12 @@ namespace AdaptiveCards::Rendering::Uwp HRESULT AddInlineShowCard(_In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveActionSet* actionSet, _In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, _In_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs); HRESULT AddInlineShowCard(_In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveCard* adaptiveCard, _In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, _In_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs); HRESULT AddOverflowButton(_In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveActionSet* actionSet, @@ -92,10 +86,7 @@ namespace AdaptiveCards::Rendering::Uwp HRESULT AddInlineShowCardHelper(UINT32 internalId, _In_ ABI::AdaptiveCards::ObjectModel::Uwp::IAdaptiveShowCardAction* showCardAction, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionButtonUIElement, - _In_ ABI::Windows::UI::Xaml::IUIElement* actionOverflowUIElement, _In_ ABI::Windows::UI::Xaml::IUIElement* showCardUIElement, - UINT32 primaryButtonIndex, _In_ ABI::AdaptiveCards::Rendering::Uwp::IAdaptiveRenderArgs* renderArgs); Microsoft::WRL::ComPtr m_originatingCard; diff --git a/source/uwp/Renderer/lib/Util.h b/source/uwp/Renderer/lib/Util.h index a79186e9cf..fd8a08d85e 100644 --- a/source/uwp/Renderer/lib/Util.h +++ b/source/uwp/Renderer/lib/Util.h @@ -221,10 +221,7 @@ typedef Microsoft::WRL::EventSource buttonUIElement; - Microsoft::WRL::ComPtr overflowUIElement; Microsoft::WRL::ComPtr cardUIElement; - UINT primaryButtonIndex; }; // Peek interface to help get implementation types from winrt interfaces diff --git a/source/uwp/UWPUITestApp/UWPUITestApp.csproj b/source/uwp/UWPUITestApp/UWPUITestApp.csproj index 07920a76be..366849442d 100644 --- a/source/uwp/UWPUITestApp/UWPUITestApp.csproj +++ b/source/uwp/UWPUITestApp/UWPUITestApp.csproj @@ -183,14 +183,14 @@ LinkedTestCards\Input.Text.ErrorMessage.json PreserveNewest - - LinkedTestCards\InputFormWithLabels.json - PreserveNewest - LinkedTestCards\Input.Toggle.json PreserveNewest + + LinkedTestCards\PrimarySecondaryShowCards.json + PreserveNewest + diff --git a/source/uwp/UWPUITests/TestHelpers.cs b/source/uwp/UWPUITests/TestHelpers.cs index 0614bd1afe..c012b706de 100644 --- a/source/uwp/UWPUITests/TestHelpers.cs +++ b/source/uwp/UWPUITests/TestHelpers.cs @@ -3,10 +3,7 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; @@ -35,6 +32,12 @@ public static UIObject FindElementByAutomationId(string automationId) return FindDescendantBy("AutomationId", automationId, Application.Instance.CoreWindow); } + public static UIObject FindPopupByName(string name) + { + // Popups are not under the CoreWindow. Look under the CoreWindow's grandparent + return FindDescendantBy("Name", name, Application.Instance.CoreWindow.Parent.Parent); + } + public static UIObject FindElementByName(string name, UIObject parent) { return FindDescendantBy("Name", name, parent); @@ -93,7 +96,7 @@ public static UIObject FindByMultiple(params object[] list) public static void GoToTestCase(string testCaseName) { var application = Application.Instance.CoreWindow; - + // If we are not in the home screen then we go to home and then click on the TestCase if (GetTitleText() != "Home") { @@ -146,7 +149,7 @@ public static void SetDateToUIElement(int year, int month, int day) // so we have to remove them before looking up for the month number int currentMonth = MonthNameToInt(monthAndYear[0]); MoveToMonth(currentMonth, month, calendarView); - + // Click on the day "16" button which in turn closes the popup FindElementByName("16", calendarView).Click(); } diff --git a/source/uwp/UWPUITests/UITest.cs b/source/uwp/UWPUITests/UITest.cs index bd6391c8d1..0c36e8836d 100644 --- a/source/uwp/UWPUITests/UITest.cs +++ b/source/uwp/UWPUITests/UITest.cs @@ -94,10 +94,62 @@ public void InputToggleTests() Assert.AreEqual("true", TestHelpers.GetInputValue("acceptTerms")); } + [TestMethod] + public void SecondaryShowCardTest() + { + TestHelpers.GoToTestCase("PrimarySecondaryShowCards"); + + // We should be able to find the primary show card action and the overflow menu + var primaryShowCardAction = TestHelpers.FindByMultiple("Name", "Primary Show Card Action", "ClassName", "Button"); + Assert.IsNotNull(primaryShowCardAction); + + var overflowMenu = TestHelpers.FindByMultiple("Name", "...", "ClassName", "Button"); + Assert.IsNotNull(overflowMenu); + + // We should not be able to find either of the show cards + Assert.ThrowsException(delegate () { TestHelpers.FindElementByName("Primary Show Card"); }); + Assert.ThrowsException(delegate () { TestHelpers.FindElementByName("Secondary Show Card"); }); + + // Click the primary show card and validate that the card appears + primaryShowCardAction.Click(); + + var primaryShowCard = TestHelpers.FindElementByName("Primary Show Card"); + Assert.IsNotNull(primaryShowCard); + Assert.ThrowsException(delegate () { TestHelpers.FindElementByName("Secondary Show Card"); }); + + // Click the overflow menu and find the secondary show card action + overflowMenu.Click(); + + var secondaryShowCardAction = TestHelpers.FindPopupByName("Secondary Show Card Action"); + Assert.IsNotNull(secondaryShowCardAction); + + // Click the secondary action and validate that the card appears + secondaryShowCardAction.Click(); + + var secondaryShowCard = TestHelpers.FindElementByName("Secondary Show Card"); + Assert.ThrowsException(delegate () { TestHelpers.FindElementByName("Primary Show Card"); }); + + // Close the secondary show card and validate the state + overflowMenu.Click(); + secondaryShowCardAction = TestHelpers.FindPopupByName("Secondary Show Card Action"); + Assert.IsNotNull(secondaryShowCardAction); + secondaryShowCardAction.Click(); + + primaryShowCardAction = TestHelpers.FindByMultiple("Name", "Primary Show Card Action", "ClassName", "Button"); + Assert.IsNotNull(primaryShowCardAction); + + overflowMenu = TestHelpers.FindByMultiple("Name", "...", "ClassName", "Button"); + Assert.IsNotNull(overflowMenu); + + Assert.ThrowsException(delegate () { TestHelpers.FindElementByName("Primary Show Card"); }); + Assert.ThrowsException(delegate () { TestHelpers.FindElementByName("Secondary Show Card"); }); + } + [ClassCleanup] public static void TearDown() { application.Close(); } } + }