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};
}
}