提交 48258a02 编写于 作者: R Rob LaDuca

Merging release/3.1 to master

......@@ -2,7 +2,7 @@
<PropertyGroup>
<_PresentationBuildTasksTfm Condition="'$(MSBuildRuntimeType)' == 'Core'">netcoreapp2.1</_PresentationBuildTasksTfm>
<_PresentationBuildTasksTfm Condition="'$(MSBuildRuntimeType)' != 'Core'">net472</_PresentationBuildTasksTfm>
<_PresentationBuildTasksAssembly Condition="'$(_PresentationBuildTasksAssembly)'==''">$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\tools\$(_PresentationBuildTasksTfm)\PresentationBuildTasks.dll'))</_PresentationBuildTasksAssembly>
<_PresentationBuildTasksAssembly Condition="'$(_PresentationBuildTasksAssembly)'==''">$([MSBuild]::Unescape($([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\tools\$(_PresentationBuildTasksTfm)\PresentationBuildTasks.dll'))))</_PresentationBuildTasksAssembly>
</PropertyGroup>
<UsingTask TaskName="Microsoft.Build.Tasks.Windows.MarkupCompilePass1" AssemblyFile="$(_PresentationBuildTasksAssembly)" />
......
......@@ -411,6 +411,14 @@ internal double MinSizeForArrange
}
}
/// <summary>
/// Returns min size, never taking into account shared state.
/// </summary>
internal double RawMinSize
{
get { return _minSize; }
}
/// <summary>
/// Offset.
/// </summary>
......@@ -872,7 +880,7 @@ private void OnLayoutUpdated(object sender, EventArgs e)
// accumulate min size of all participating definitions
for (int i = 0, count = _registry.Count; i < count; ++i)
{
sharedMinSize = Math.Max(sharedMinSize, _registry[i].MinSize);
sharedMinSize = Math.Max(sharedMinSize, _registry[i]._minSize);
}
bool sharedMinSizeChanged = !DoubleUtil.AreClose(_minSize, sharedMinSize);
......@@ -882,32 +890,66 @@ private void OnLayoutUpdated(object sender, EventArgs e)
{
DefinitionBase definitionBase = _registry[i];
if (sharedMinSizeChanged || definitionBase.LayoutWasUpdated)
// we'll set d.UseSharedMinimum to maintain the invariant:
// d.UseSharedMinimum iff d._minSize < this.MinSize
// i.e. iff d is not a "long-pole" definition.
//
// Measure/Arrange of d's Grid uses d._minSize for long-pole
// definitions, and max(d._minSize, shared size) for
// short-pole definitions. This distinction allows us to react
// to changes in "long-pole-ness" more efficiently and correctly,
// by avoiding remeasures when a long-pole definition changes.
bool useSharedMinimum = !DoubleUtil.AreClose(definitionBase._minSize, sharedMinSize);
// before doing that, determine whether d's Grid needs to be remeasured.
// It's important _not_ to remeasure if the last measure is still
// valid, otherwise infinite loops are possible
bool measureIsValid;
if (!definitionBase.UseSharedMinimum)
{
// if definition's min size is different, then need to re-measure
if (!DoubleUtil.AreClose(sharedMinSize, definitionBase.MinSize))
// d was a long-pole. measure is valid iff it's still a long-pole,
// since previous measure didn't use shared size.
measureIsValid = !useSharedMinimum;
}
else if (useSharedMinimum)
{
Grid parentGrid = (Grid)definitionBase.Parent;
parentGrid.InvalidateMeasure();
definitionBase.UseSharedMinimum = true;
// d was a short-pole, and still is. measure is valid
// iff the shared size didn't change
measureIsValid = !sharedMinSizeChanged;
}
else
{
definitionBase.UseSharedMinimum = false;
// d was a short-pole, but is now a long-pole. This can
// happen in several ways:
// a. d's minSize increased to or past the old shared size
// b. other long-pole definitions decreased, leaving
// d as the new winner
// In the former case, the measure is valid - it used
// d's new larger minSize. In the latter case, the
// measure is invalid - it used the old shared size,
// which is larger than d's (possibly changed) minSize
measureIsValid = (definitionBase.LayoutWasUpdated &&
DoubleUtil.GreaterThanOrClose(definitionBase._minSize, this.MinSize));
}
if (!measureIsValid)
{
Grid parentGrid = (Grid)definitionBase.Parent;
parentGrid.InvalidateMeasure();
}
else if (!DoubleUtil.AreClose(sharedMinSize, definitionBase.SizeCache))
{
// if measure is valid then also need to check arrange.
// Note: definitionBase.SizeCache is volatile but at this point
// it contains up-to-date final size
if (!DoubleUtil.AreClose(sharedMinSize, definitionBase.SizeCache))
{
Grid parentGrid = (Grid)definitionBase.Parent;
parentGrid.InvalidateArrange();
}
}
// now we can restore the invariant, and clear the layout flag
definitionBase.UseSharedMinimum = useSharedMinimum;
definitionBase.LayoutWasUpdated = false;
}
}
_minSize = sharedMinSize;
......
......@@ -1178,11 +1178,11 @@ private double[] CacheMinSizes(int cellsHead, bool isRows)
{
if (isRows)
{
minSizes[PrivateCells[i].RowIndex] = DefinitionsV[PrivateCells[i].RowIndex].MinSize;
minSizes[PrivateCells[i].RowIndex] = DefinitionsV[PrivateCells[i].RowIndex].RawMinSize;
}
else
{
minSizes[PrivateCells[i].ColumnIndex] = DefinitionsU[PrivateCells[i].ColumnIndex].MinSize;
minSizes[PrivateCells[i].ColumnIndex] = DefinitionsU[PrivateCells[i].ColumnIndex].RawMinSize;
}
i = PrivateCells[i].Next;
......
......@@ -3011,7 +3011,7 @@ private bool IsOnCurrentPage(FrameworkElement viewPort, FrameworkElement element
Rect viewPortBounds = new Rect(new Point(), viewPort.RenderSize);
Rect elementBounds = new Rect(new Point(), element.RenderSize);
elementBounds = element.TransformToAncestor(viewPort).TransformBounds(elementBounds);
elementBounds = CorrectCatastrophicCancellation(element.TransformToAncestor(viewPort)).TransformBounds(elementBounds);
bool northSouth = (axis == FocusNavigationDirection.Up || axis == FocusNavigationDirection.Down);
bool eastWest = (axis == FocusNavigationDirection.Left || axis == FocusNavigationDirection.Right);
......@@ -3078,6 +3078,46 @@ private bool IsOnCurrentPage(FrameworkElement viewPort, FrameworkElement element
return ElementViewportPosition.None;
}
// in large virtualized hierarchical lists (TreeView or grouping), the transform
// returned by element.TransformToAncestor(viewport) is vulnerable to catastrophic
// cancellation. If element is at the top of the viewport, but embedded in
// layers of the hierarchy, the contributions of the intermediate elements add
// up to a large positive number which should exactly cancel out the large
// negative offset of the viewport's direct child to produce net offset of 0.0.
// But floating-point drift while accumulating the intermediate offsets and
// catastrophic cancellation in the last step may produce a very small
// non-zero number instead (e.g. -0.0000000000006548). This can lead to
// infinite loops and incorrect decisions in layout.
// To mitigate this problem, replace near-zero offsets with zero.
private static GeneralTransform CorrectCatastrophicCancellation(GeneralTransform transform)
{
MatrixTransform matrixTransform = transform as MatrixTransform;
if (matrixTransform != null)
{
bool needNewTransform = false;
Matrix matrix = matrixTransform.Matrix;
if (matrix.OffsetX != 0.0 && LayoutDoubleUtil.AreClose(matrix.OffsetX, 0.0))
{
matrix.OffsetX = 0.0;
needNewTransform = true;
}
if (matrix.OffsetY != 0.0 && LayoutDoubleUtil.AreClose(matrix.OffsetY, 0.0))
{
matrix.OffsetY = 0.0;
needNewTransform = true;
}
if (needNewTransform)
{
transform = new MatrixTransform(matrix);
}
}
return transform;
}
private static bool ElementIntersectsViewport(Rect viewportRect, Rect elementRect)
{
if (viewportRect.IsEmpty || elementRect.IsEmpty)
......
......@@ -5550,7 +5550,7 @@ private void CoerceScrollingViewportOffset(ref Rect viewport, Size extent, bool
bool areContainersUniformlySized,
double uniformOrAverageContainerSize)
{
if (firstContainer == null)
if (firstContainer == null || IsViewportEmpty(isHorizontal, viewport))
{
return -1.0; // undefined if no children in view
}
......
......@@ -1059,7 +1059,11 @@ public static bool IsOurWindow(IntPtr hwnd, DependencyObject element)
// popups when both active source and current captured is
// null due to clicking some where else should be handled by
// click through event handler.
ReacquireCapture(targetCapture, targetFocus);
if (!ReacquireCapture(targetCapture, targetFocus))
{
// call the setter if we couldn't reacquire capture
setter(false);
}
e.Handled = true;
}
else
......@@ -1078,7 +1082,11 @@ public static bool IsOurWindow(IntPtr hwnd, DependencyObject element)
{
// If a descendant of targetCapture is losing capture
// then take capture on targetCapture
ReacquireCapture(targetCapture, targetFocus);
if (!ReacquireCapture(targetCapture, targetFocus))
{
// call the setter if we couldn't reacquire capture
setter(false);
}
e.Handled = true;
}
else if (!IsCaptureInSubtree(targetCapture))
......@@ -1092,13 +1100,14 @@ public static bool IsOurWindow(IntPtr hwnd, DependencyObject element)
}
}
private static void ReacquireCapture(UIElement targetCapture, UIElement targetFocus)
private static bool ReacquireCapture(UIElement targetCapture, UIElement targetFocus)
{
Mouse.Capture(targetCapture, CaptureMode.SubTree);
if (targetFocus != null && !targetFocus.IsKeyboardFocusWithin)
bool success = Mouse.Capture(targetCapture, CaptureMode.SubTree);
if (success && targetFocus != null && !targetFocus.IsKeyboardFocusWithin)
{
targetFocus.Focus();
}
return success;
}
public static bool IsMousePhysicallyOver(UIElement element)
......
......@@ -1513,7 +1513,10 @@ private void OnGotKeyboardFocusThunk(KeyboardFocusChangedEventArgs e)
{
// Call base.OnIsKeyboardFocusWithinChanged only if the new focus
// is not a direct descendant of menu button.
// It's possible to get here when disabled, which can lead to a
// focus war resulting in StackOverflow. Don't start the war.
if (e.OriginalSource != this &&
this.IsEnabled &&
!TreeHelper.IsVisualAncestorOf(this, e.OriginalSource as DependencyObject))
{
BaseOnIsKeyboardFocusWithin();
......@@ -1528,13 +1531,19 @@ protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
if (ribbonCurrentSelection != null &&
IsDropDownOpen)
{
// If the drop down is open and the ribbonCurrentSelection is valid
// but still popup doesnt have focus within,
// then focus the current selection.
// It's possible to get here when disabled, or when an app explicitly
// moves focus in a GotKeyboardFocus handler called earlier in the
// bubbling route. Either of these can lead to a
// focus war resulting in StackOverflow. Don't start the war
UIElement popupChild = _popup.TryGetChild();
if (popupChild != null &&
this.IsEnabled &&
this.IsKeyboardFocusWithin &&
!popupChild.IsKeyboardFocusWithin)
{
// If the drop down is open and the ribbonCurrentSelection is valid
// but still popup doesnt have focus within,
// then focus the current selection.
ribbonCurrentSelection.Focus();
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册