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

Merging release/3.1 to master

...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<_PresentationBuildTasksTfm Condition="'$(MSBuildRuntimeType)' == 'Core'">netcoreapp2.1</_PresentationBuildTasksTfm> <_PresentationBuildTasksTfm Condition="'$(MSBuildRuntimeType)' == 'Core'">netcoreapp2.1</_PresentationBuildTasksTfm>
<_PresentationBuildTasksTfm Condition="'$(MSBuildRuntimeType)' != 'Core'">net472</_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> </PropertyGroup>
<UsingTask TaskName="Microsoft.Build.Tasks.Windows.MarkupCompilePass1" AssemblyFile="$(_PresentationBuildTasksAssembly)" /> <UsingTask TaskName="Microsoft.Build.Tasks.Windows.MarkupCompilePass1" AssemblyFile="$(_PresentationBuildTasksAssembly)" />
......
...@@ -411,6 +411,14 @@ internal double MinSizeForArrange ...@@ -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> /// <summary>
/// Offset. /// Offset.
/// </summary> /// </summary>
...@@ -872,7 +880,7 @@ private void OnLayoutUpdated(object sender, EventArgs e) ...@@ -872,7 +880,7 @@ private void OnLayoutUpdated(object sender, EventArgs e)
// accumulate min size of all participating definitions // accumulate min size of all participating definitions
for (int i = 0, count = _registry.Count; i < count; ++i) 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); bool sharedMinSizeChanged = !DoubleUtil.AreClose(_minSize, sharedMinSize);
...@@ -882,32 +890,66 @@ private void OnLayoutUpdated(object sender, EventArgs e) ...@@ -882,32 +890,66 @@ private void OnLayoutUpdated(object sender, EventArgs e)
{ {
DefinitionBase definitionBase = _registry[i]; 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 // d was a long-pole. measure is valid iff it's still a long-pole,
if (!DoubleUtil.AreClose(sharedMinSize, definitionBase.MinSize)) // since previous measure didn't use shared size.
measureIsValid = !useSharedMinimum;
}
else if (useSharedMinimum)
{ {
Grid parentGrid = (Grid)definitionBase.Parent; // d was a short-pole, and still is. measure is valid
parentGrid.InvalidateMeasure(); // iff the shared size didn't change
definitionBase.UseSharedMinimum = true; measureIsValid = !sharedMinSizeChanged;
} }
else 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. // if measure is valid then also need to check arrange.
// Note: definitionBase.SizeCache is volatile but at this point // Note: definitionBase.SizeCache is volatile but at this point
// it contains up-to-date final size // it contains up-to-date final size
if (!DoubleUtil.AreClose(sharedMinSize, definitionBase.SizeCache))
{
Grid parentGrid = (Grid)definitionBase.Parent; Grid parentGrid = (Grid)definitionBase.Parent;
parentGrid.InvalidateArrange(); parentGrid.InvalidateArrange();
} }
}
// now we can restore the invariant, and clear the layout flag
definitionBase.UseSharedMinimum = useSharedMinimum;
definitionBase.LayoutWasUpdated = false; definitionBase.LayoutWasUpdated = false;
} }
}
_minSize = sharedMinSize; _minSize = sharedMinSize;
......
...@@ -1178,11 +1178,11 @@ private double[] CacheMinSizes(int cellsHead, bool isRows) ...@@ -1178,11 +1178,11 @@ private double[] CacheMinSizes(int cellsHead, bool isRows)
{ {
if (isRows) if (isRows)
{ {
minSizes[PrivateCells[i].RowIndex] = DefinitionsV[PrivateCells[i].RowIndex].MinSize; minSizes[PrivateCells[i].RowIndex] = DefinitionsV[PrivateCells[i].RowIndex].RawMinSize;
} }
else else
{ {
minSizes[PrivateCells[i].ColumnIndex] = DefinitionsU[PrivateCells[i].ColumnIndex].MinSize; minSizes[PrivateCells[i].ColumnIndex] = DefinitionsU[PrivateCells[i].ColumnIndex].RawMinSize;
} }
i = PrivateCells[i].Next; i = PrivateCells[i].Next;
......
...@@ -3011,7 +3011,7 @@ private bool IsOnCurrentPage(FrameworkElement viewPort, FrameworkElement element ...@@ -3011,7 +3011,7 @@ private bool IsOnCurrentPage(FrameworkElement viewPort, FrameworkElement element
Rect viewPortBounds = new Rect(new Point(), viewPort.RenderSize); Rect viewPortBounds = new Rect(new Point(), viewPort.RenderSize);
Rect elementBounds = new Rect(new Point(), element.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 northSouth = (axis == FocusNavigationDirection.Up || axis == FocusNavigationDirection.Down);
bool eastWest = (axis == FocusNavigationDirection.Left || axis == FocusNavigationDirection.Right); bool eastWest = (axis == FocusNavigationDirection.Left || axis == FocusNavigationDirection.Right);
...@@ -3078,6 +3078,46 @@ private bool IsOnCurrentPage(FrameworkElement viewPort, FrameworkElement element ...@@ -3078,6 +3078,46 @@ private bool IsOnCurrentPage(FrameworkElement viewPort, FrameworkElement element
return ElementViewportPosition.None; 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) private static bool ElementIntersectsViewport(Rect viewportRect, Rect elementRect)
{ {
if (viewportRect.IsEmpty || elementRect.IsEmpty) if (viewportRect.IsEmpty || elementRect.IsEmpty)
......
...@@ -5550,7 +5550,7 @@ private void CoerceScrollingViewportOffset(ref Rect viewport, Size extent, bool ...@@ -5550,7 +5550,7 @@ private void CoerceScrollingViewportOffset(ref Rect viewport, Size extent, bool
bool areContainersUniformlySized, bool areContainersUniformlySized,
double uniformOrAverageContainerSize) double uniformOrAverageContainerSize)
{ {
if (firstContainer == null) if (firstContainer == null || IsViewportEmpty(isHorizontal, viewport))
{ {
return -1.0; // undefined if no children in view return -1.0; // undefined if no children in view
} }
......
...@@ -1059,7 +1059,11 @@ public static bool IsOurWindow(IntPtr hwnd, DependencyObject element) ...@@ -1059,7 +1059,11 @@ public static bool IsOurWindow(IntPtr hwnd, DependencyObject element)
// popups when both active source and current captured is // popups when both active source and current captured is
// null due to clicking some where else should be handled by // null due to clicking some where else should be handled by
// click through event handler. // 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; e.Handled = true;
} }
else else
...@@ -1078,7 +1082,11 @@ public static bool IsOurWindow(IntPtr hwnd, DependencyObject element) ...@@ -1078,7 +1082,11 @@ public static bool IsOurWindow(IntPtr hwnd, DependencyObject element)
{ {
// If a descendant of targetCapture is losing capture // If a descendant of targetCapture is losing capture
// then take capture on targetCapture // 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; e.Handled = true;
} }
else if (!IsCaptureInSubtree(targetCapture)) else if (!IsCaptureInSubtree(targetCapture))
...@@ -1092,13 +1100,14 @@ public static bool IsOurWindow(IntPtr hwnd, DependencyObject element) ...@@ -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); bool success = Mouse.Capture(targetCapture, CaptureMode.SubTree);
if (targetFocus != null && !targetFocus.IsKeyboardFocusWithin) if (success && targetFocus != null && !targetFocus.IsKeyboardFocusWithin)
{ {
targetFocus.Focus(); targetFocus.Focus();
} }
return success;
} }
public static bool IsMousePhysicallyOver(UIElement element) public static bool IsMousePhysicallyOver(UIElement element)
......
...@@ -1513,7 +1513,10 @@ private void OnGotKeyboardFocusThunk(KeyboardFocusChangedEventArgs e) ...@@ -1513,7 +1513,10 @@ private void OnGotKeyboardFocusThunk(KeyboardFocusChangedEventArgs e)
{ {
// Call base.OnIsKeyboardFocusWithinChanged only if the new focus // Call base.OnIsKeyboardFocusWithinChanged only if the new focus
// is not a direct descendant of menu button. // 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 && if (e.OriginalSource != this &&
this.IsEnabled &&
!TreeHelper.IsVisualAncestorOf(this, e.OriginalSource as DependencyObject)) !TreeHelper.IsVisualAncestorOf(this, e.OriginalSource as DependencyObject))
{ {
BaseOnIsKeyboardFocusWithin(); BaseOnIsKeyboardFocusWithin();
...@@ -1528,13 +1531,19 @@ protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e) ...@@ -1528,13 +1531,19 @@ protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
if (ribbonCurrentSelection != null && if (ribbonCurrentSelection != null &&
IsDropDownOpen) 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(); UIElement popupChild = _popup.TryGetChild();
if (popupChild != null && if (popupChild != null &&
this.IsEnabled &&
this.IsKeyboardFocusWithin &&
!popupChild.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(); ribbonCurrentSelection.Focus();
} }
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册