in dev/ScrollPresenter/ScrollPresenter.cpp [663:962]
winrt::Size ScrollPresenter::ArrangeOverride(winrt::Size const& finalSize)
{
SCROLLPRESENTER_TRACE_INFO(*this, TRACE_MSG_METH_STR_FLT_FLT, METH_NAME, this, L"finalSize", finalSize.Width, finalSize.Height);
const winrt::UIElement content = Content();
winrt::Rect finalContentRect{};
// Possible cases:
// 1. m_availableSize is infinite, the ScrollPresenter is not constrained and takes its Content DesiredSize.
// viewport thus is finalSize.
// 2. m_availableSize > finalSize, the ScrollPresenter is constrained and its Content is smaller than the available size.
// No matter the ScrollPresenter's alignment, it does not grow larger than finalSize. viewport is finalSize again.
// 3. m_availableSize <= finalSize, the ScrollPresenter is constrained and its Content is larger than or equal to
// the available size. viewport is the smaller & constrained m_availableSize.
const winrt::Size viewport =
{
std::min(finalSize.Width, m_availableSize.Width),
std::min(finalSize.Height, m_availableSize.Height)
};
bool renderSizeChanged = false;
double newUnzoomedExtentWidth = 0.0;
double newUnzoomedExtentHeight = 0.0;
if (content)
{
float contentLayoutOffsetXDelta = 0.0f;
float contentLayoutOffsetYDelta = 0.0f;
bool isAnchoringElementHorizontally = false;
bool isAnchoringElementVertically = false;
bool isAnchoringFarEdgeHorizontally = false;
bool isAnchoringFarEdgeVertically = false;
const winrt::Size oldRenderSize = content.RenderSize();
winrt::Size contentArrangeSize = content.DesiredSize();
const winrt::FrameworkElement contentAsFE = content.try_as<winrt::FrameworkElement>();
const winrt::Thickness contentMargin = [contentAsFE]()
{
return contentAsFE ? contentAsFE.Margin() : winrt::Thickness{ 0 };
}();
const bool wasContentArrangeWidthStretched = [contentAsFE, contentArrangeSize, viewport]()
{
return contentAsFE &&
contentAsFE.HorizontalAlignment() == winrt::HorizontalAlignment::Stretch &&
isnan(contentAsFE.Width()) &&
contentArrangeSize.Width < viewport.Width;
}();
const bool wasContentArrangeHeightStretched = [contentAsFE, contentArrangeSize, viewport]()
{
return contentAsFE &&
contentAsFE.VerticalAlignment() == winrt::VerticalAlignment::Stretch &&
isnan(contentAsFE.Height()) &&
contentArrangeSize.Height < viewport.Height;
}();
if (wasContentArrangeWidthStretched)
{
// Allow the content to stretch up to the larger viewport width.
contentArrangeSize.Width = viewport.Width;
}
if (wasContentArrangeHeightStretched)
{
// Allow the content to stretch up to the larger viewport height.
contentArrangeSize.Height = viewport.Height;
}
finalContentRect =
{
m_contentLayoutOffsetX,
m_contentLayoutOffsetY,
contentArrangeSize.Width,
contentArrangeSize.Height
};
IsAnchoring(&isAnchoringElementHorizontally, &isAnchoringElementVertically, &isAnchoringFarEdgeHorizontally, &isAnchoringFarEdgeVertically);
MUX_ASSERT(!(isAnchoringElementHorizontally && isAnchoringFarEdgeHorizontally));
MUX_ASSERT(!(isAnchoringElementVertically && isAnchoringFarEdgeVertically));
if (isAnchoringElementHorizontally || isAnchoringElementVertically || isAnchoringFarEdgeHorizontally || isAnchoringFarEdgeVertically)
{
MUX_ASSERT(m_interactionTracker);
winrt::Size preArrangeViewportToElementAnchorPointsDistance{ FloatUtil::NaN, FloatUtil::NaN };
if (isAnchoringElementHorizontally || isAnchoringElementVertically)
{
EnsureAnchorElementSelection();
preArrangeViewportToElementAnchorPointsDistance = ComputeViewportToElementAnchorPointsDistance(
m_viewportWidth,
m_viewportHeight,
true /*isForPreArrange*/);
}
else
{
ResetAnchorElement();
}
contentArrangeSize = ArrangeContent(
content,
contentMargin,
finalContentRect,
wasContentArrangeWidthStretched,
wasContentArrangeHeightStretched);
if (!isnan(preArrangeViewportToElementAnchorPointsDistance.Width) || !isnan(preArrangeViewportToElementAnchorPointsDistance.Height))
{
// Using the new viewport sizes to handle the cases where an adjustment needs to be performed because of a ScrollPresenter size change.
const winrt::Size postArrangeViewportToElementAnchorPointsDistance = ComputeViewportToElementAnchorPointsDistance(
viewport.Width /*viewportWidth*/,
viewport.Height /*viewportHeight*/,
false /*isForPreArrange*/);
if (isAnchoringElementHorizontally &&
!isnan(preArrangeViewportToElementAnchorPointsDistance.Width) &&
!isnan(postArrangeViewportToElementAnchorPointsDistance.Width) &&
preArrangeViewportToElementAnchorPointsDistance.Width != postArrangeViewportToElementAnchorPointsDistance.Width)
{
// Perform horizontal offset adjustment due to element anchoring
contentLayoutOffsetXDelta = ComputeContentLayoutOffsetDelta(
ScrollPresenterDimension::HorizontalScroll,
postArrangeViewportToElementAnchorPointsDistance.Width - preArrangeViewportToElementAnchorPointsDistance.Width /*unzoomedDelta*/);
}
if (isAnchoringElementVertically &&
!isnan(preArrangeViewportToElementAnchorPointsDistance.Height) &&
!isnan(postArrangeViewportToElementAnchorPointsDistance.Height) &&
preArrangeViewportToElementAnchorPointsDistance.Height != postArrangeViewportToElementAnchorPointsDistance.Height)
{
// Perform vertical offset adjustment due to element anchoring
contentLayoutOffsetYDelta = ComputeContentLayoutOffsetDelta(
ScrollPresenterDimension::VerticalScroll,
postArrangeViewportToElementAnchorPointsDistance.Height - preArrangeViewportToElementAnchorPointsDistance.Height /*unzoomedDelta*/);
}
}
}
else
{
ResetAnchorElement();
contentArrangeSize = ArrangeContent(
content,
contentMargin,
finalContentRect,
wasContentArrangeWidthStretched,
wasContentArrangeHeightStretched);
}
newUnzoomedExtentWidth = contentArrangeSize.Width;
newUnzoomedExtentHeight = contentArrangeSize.Height;
double maxUnzoomedExtentWidth = std::numeric_limits<double>::infinity();
double maxUnzoomedExtentHeight = std::numeric_limits<double>::infinity();
if (contentAsFE)
{
// Determine the maximum size directly set on the content, if any.
maxUnzoomedExtentWidth = GetComputedMaxWidth(maxUnzoomedExtentWidth, contentAsFE);
maxUnzoomedExtentHeight = GetComputedMaxHeight(maxUnzoomedExtentHeight, contentAsFE);
}
// Take into account the actual resulting rendering size, in case it's larger than the desired size.
// But the extent must not exceed the size explicitly set on the content, if any.
newUnzoomedExtentWidth = std::max(
newUnzoomedExtentWidth,
std::max(0.0, content.RenderSize().Width + contentMargin.Left + contentMargin.Right));
newUnzoomedExtentWidth = std::min(
newUnzoomedExtentWidth,
maxUnzoomedExtentWidth);
newUnzoomedExtentHeight = std::max(
newUnzoomedExtentHeight,
std::max(0.0, content.RenderSize().Height + contentMargin.Top + contentMargin.Bottom));
newUnzoomedExtentHeight = std::min(
newUnzoomedExtentHeight,
maxUnzoomedExtentHeight);
if (isAnchoringFarEdgeHorizontally)
{
float unzoomedDelta = 0.0f;
if (newUnzoomedExtentWidth > m_unzoomedExtentWidth || // ExtentWidth grew
m_zoomedHorizontalOffset + m_viewportWidth > m_zoomFactor* m_unzoomedExtentWidth) // ExtentWidth shrank while overpanning
{
// Perform horizontal offset adjustment due to edge anchoring
unzoomedDelta = static_cast<float>(newUnzoomedExtentWidth - m_unzoomedExtentWidth);
}
if (static_cast<float>(m_viewportWidth) > viewport.Width)
{
// Viewport width shrank: Perform horizontal offset adjustment due to edge anchoring
unzoomedDelta += (static_cast<float>(m_viewportWidth) - viewport.Width) / m_zoomFactor;
}
if (unzoomedDelta != 0.0f)
{
MUX_ASSERT(contentLayoutOffsetXDelta == 0.0f);
contentLayoutOffsetXDelta = ComputeContentLayoutOffsetDelta(ScrollPresenterDimension::HorizontalScroll, unzoomedDelta);
}
}
if (isAnchoringFarEdgeVertically)
{
float unzoomedDelta = 0.0f;
if (newUnzoomedExtentHeight > m_unzoomedExtentHeight || // ExtentHeight grew
m_zoomedVerticalOffset + m_viewportHeight > m_zoomFactor* m_unzoomedExtentHeight) // ExtentHeight shrank while overpanning
{
// Perform vertical offset adjustment due to edge anchoring
unzoomedDelta = static_cast<float>(newUnzoomedExtentHeight - m_unzoomedExtentHeight);
}
if (static_cast<float>(m_viewportHeight) > viewport.Height)
{
// Viewport height shrank: Perform vertical offset adjustment due to edge anchoring
unzoomedDelta += (static_cast<float>(m_viewportHeight) - viewport.Height) / m_zoomFactor;
}
if (unzoomedDelta != 0.0f)
{
MUX_ASSERT(contentLayoutOffsetYDelta == 0.0f);
contentLayoutOffsetYDelta = ComputeContentLayoutOffsetDelta(ScrollPresenterDimension::VerticalScroll, unzoomedDelta);
}
}
if (contentLayoutOffsetXDelta != 0.0f || contentLayoutOffsetYDelta != 0.0f)
{
const winrt::Rect contentRectWithDelta =
{
m_contentLayoutOffsetX + contentLayoutOffsetXDelta,
m_contentLayoutOffsetY + contentLayoutOffsetYDelta,
contentArrangeSize.Width,
contentArrangeSize.Height
};
SCROLLPRESENTER_TRACE_INFO(*this, TRACE_MSG_METH_STR_STR, METH_NAME, this, L"content Arrange", TypeLogging::RectToString(contentRectWithDelta).c_str());
content.Arrange(contentRectWithDelta);
if (contentLayoutOffsetXDelta != 0.0f)
{
m_contentLayoutOffsetX += contentLayoutOffsetXDelta;
UpdateOffset(ScrollPresenterDimension::HorizontalScroll, m_zoomedHorizontalOffset - contentLayoutOffsetXDelta);
OnContentLayoutOffsetChanged(ScrollPresenterDimension::HorizontalScroll);
}
if (contentLayoutOffsetYDelta != 0.0f)
{
m_contentLayoutOffsetY += contentLayoutOffsetYDelta;
UpdateOffset(ScrollPresenterDimension::VerticalScroll, m_zoomedVerticalOffset - contentLayoutOffsetYDelta);
OnContentLayoutOffsetChanged(ScrollPresenterDimension::VerticalScroll);
}
OnViewChanged(contentLayoutOffsetXDelta != 0.0f /*horizontalOffsetChanged*/, contentLayoutOffsetYDelta != 0.0f /*verticalOffsetChanged*/);
}
renderSizeChanged = content.RenderSize() != oldRenderSize;
}
// Set a rectangular clip on this ScrollPresenter the same size as the arrange
// rectangle so the content does not render beyond it.
auto rectangleGeometry = Clip().as<winrt::RectangleGeometry>();
if (!rectangleGeometry)
{
// Ensure that this ScrollPresenter has a rectangular clip.
winrt::RectangleGeometry newRectangleGeometry;
newRectangleGeometry.Rect();
Clip(newRectangleGeometry);
rectangleGeometry = newRectangleGeometry;
}
const winrt::Rect newClipRect{ 0.0f, 0.0f, viewport.Width, viewport.Height };
rectangleGeometry.Rect(newClipRect);
UpdateUnzoomedExtentAndViewport(
renderSizeChanged,
newUnzoomedExtentWidth /*unzoomedExtentWidth*/,
newUnzoomedExtentHeight /*unzoomedExtentHeight*/,
viewport.Width /*viewportWidth*/,
viewport.Height /*viewportHeight*/);
// We do the following only when effective viewport
// support is not available. This is to provide downlevel support.
if (SharedHelpers::IsRS5OrHigher())
{
m_isAnchorElementDirty = true;
}
else
{
ClearAnchorCandidates();
RaisePostArrange();
}
return viewport;
}