winrt::Size WholeItemsPanel::MeasureOverride()

in source/uwp/Renderer/lib/WholeItemsPanel.cpp [19:193]


    winrt::Size WholeItemsPanel::MeasureOverride(winrt::Size const& availableSize)
    {
        unsigned int count{};
        float currentHeight{};
        float maxDesiredWidth{};
        bool visible{true};

        auto children = this->Children();
        count = children.Size();

        const winrt::Size noVerticalLimit{availableSize.Width, std::numeric_limits<float>::infinity()};
        auto measuredAvailableSize{availableSize};

        m_visibleCount = count;

        for (uint32_t i = 0; i < count; i++)
        {
            auto child = children.GetAt(i);
            child.Measure(noVerticalLimit);

            if (visible)
            {
                winrt::Size childSize = child.DesiredSize();

                float newHeight = currentHeight + childSize.Height;

                if (newHeight > measuredAvailableSize.Height)
                {
                    const double availableHeightForItem = measuredAvailableSize.Height - currentHeight;
                    bool keepItem = m_isMainPanel && (i == 0); // by default, only keep the first item in the main panel.

                    // Item does not fit
                    // 1. We mark the current panel as truncated
                    //    2.1 If the child is a panel: always
                    //    2.2 If the child is a text block: only if it cannot meet its minlines constraint (default is
                    //    1) 2.3 If the child is a image or a shape (circular cropped image):
                    //            - stretched images might be resized

                    if (auto childAsPanel = child.try_as<winrt::Panel>())
                    {
                        m_isTruncated = true;

                        // There is a tricky scenario here: a subgroup might have asked for more size than available while
                        // Still being able to reduce its size, for example if it has a minSize which can be respected
                        if (!keepItem)
                        {
                            winrt::Size remainingSpace = {measuredAvailableSize.Width, static_cast<float>(availableHeightForItem)};

                            child.Measure(remainingSpace);

                            m_isTruncated = IsAnySubgroupTruncated(childAsPanel);
                        }
                    }
                    if (auto childAsTextBlock = child.try_as<winrt::TextBlock>())
                    {
                        winrt::TextWrapping currentWrap = winrt::TextWrapping::Wrap;
                        if (childAsTextBlock.TextWrapping() == winrt::TextWrapping::NoWrap)
                        {
                            // If the text already does not wrap, it should not be displayed
                            // Unless it is the first item of the group
                            m_isTruncated = true;
                        }
                        else
                        {
                            // For wrapping TextBlocks, we have to check if the minlines property could be respected
                            // In order to do this, we remove the wrapping:
                            //   1. if the textblock has a min lines constraint, this will remain as it is implemented with MinHeight
                            //   2. if the textblock has no min lines, constraint, this will measure a single line, which is the default minlines
                            winrt::Size noLimit{std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity()};
                            childAsTextBlock.TextWrapping(winrt::TextWrapping::NoWrap);
                            childAsTextBlock.Measure(noLimit);
                            childSize = childAsTextBlock.DesiredSize();
                            currentWrap = childAsTextBlock.TextWrapping();

                            // If the single line fits, let's keep the textblock
                            // In any case, we keep the first item of the first group
                            if (childSize.Height <= availableHeightForItem)
                            {
                                keepItem = true;
                            }
                            else
                            {
                                if (m_isMainPanel)
                                {
                                    keepItem = true;

                                    // Text did not reach its minHeight property and we have to clear it otherwise
                                    // ellipses won't be displayed correctly...
                                    winrt::IFrameworkElementStatics frameworkElementStatics;
                                    child.ClearValue(frameworkElementStatics.MinHeightProperty());
                                }
                                else
                                {
                                    keepItem = false;
                                    // we must measure it a last time as we have changed its properties
                                    // if we keep it, it will be measured again with the exact remaining space
                                    child.Measure(noVerticalLimit);
                                }
                            }
                            m_isTruncated = !keepItem;
                        }
                    }
                    if (auto childAsImage = child.try_as<winrt::Image>())
                    {
                        if (!HasExplicitSize(childAsImage))
                        {
                            childAsImage.Stretch(winrt::Stretch::Uniform);
                            keepItem = true;
                            m_isTruncated = false;
                        }
                        else
                        {
                            m_isTruncated = true;
                        }
                    }
                    if (auto childAsShape = child.try_as<winrt::Shape>())
                    {
                        if (!HasExplicitSize(childAsShape))
                        {
                            LayoutCroppedImage(childAsShape, measuredAvailableSize.Width, measuredAvailableSize.Height - currentHeight);
                            keepItem = true;
                            m_isTruncated = false;
                        }
                        else
                        {
                            m_isTruncated = true;
                        }
                    }
                    if (keepItem)
                    {
                        // Let's simply give the child the remaining space
                        measuredAvailableSize.Height = measuredAvailableSize.Height - currentHeight;
                        child.Measure(measuredAvailableSize);
                        childSize = child.DesiredSize();

                        m_visibleCount = i + 1;
                        currentHeight = std::max((currentHeight + childSize.Height), measuredAvailableSize.Height);
                        maxDesiredWidth = std::max(childSize.Width, maxDesiredWidth);
                    }
                    else
                    {
                        // Only display previous items
                        m_visibleCount = i;
                    }
                    visible = false;
                    continue;
                }
                currentHeight = newHeight;
                maxDesiredWidth = std::max(childSize.Width, maxDesiredWidth);
            }
        }

        m_calculatedSize = currentHeight;

        if (m_visibleCount == count)
        {
            m_allElementsRendered = true;
        }
        else // In the first pass, all the contents will always fit
        {
            m_allElementsRendered = false;
        }

        // If inside an infinity/auto width container
        if (measuredAvailableSize.Width == std::numeric_limits<float>::infinity())
        {
            // We use the calculated max desired width of children
            return {maxDesiredWidth, currentHeight};
        }
        else
        {
            // Otherwise we match the fixed width of the container
            return {measuredAvailableSize.Width, currentHeight};
        }
    }