Exemplo n.º 1
0
        /// <summary>
        /// Calculates the value to be used for layout rounding at high DPI.
        /// </summary>
        /// <param name="value">Input value to be rounded.</param>
        /// <param name="dpiScale">Ratio of screen's DPI to layout DPI</param>
        /// <returns>Adjusted value that will produce layout rounding on screen at high dpi.</returns>
        /// <remarks>
        /// This is a layout helper method. It takes DPI into account and also does not return
        /// the rounded value if it is unacceptable for layout, e.g. Infinity or NaN. It's a helper
        /// associated with the UseLayoutRounding property and should not be used as a general rounding
        /// utility.
        /// </remarks>
        public static double RoundLayoutValue(double value, double dpiScale)
        {
            double newValue;

            // If DPI == 1, don't use DPI-aware rounding.
            if (!MathUtilities.IsOne(dpiScale))
            {
                newValue = Math.Round(value * dpiScale) / dpiScale;

                // If rounding produces a value unacceptable to layout (NaN, Infinity or MaxValue),
                // use the original value.
                if (double.IsNaN(newValue) ||
                    double.IsInfinity(newValue) ||
                    MathUtilities.AreClose(newValue, double.MaxValue))
                {
                    newValue = value;
                }
            }
            else
            {
                newValue = Math.Round(value);
            }

            return(newValue);
        }
Exemplo n.º 2
0
        public void Two_Equivalent_Double_Values_Are_Close()
        {
            var actual = MathUtilities.AreClose(AnyValue, _calculatedAnyValue);

            Assert.True(actual);
            Assert.Equal(AnyValue, Math.Round(_calculatedAnyValue, 14));
        }
Exemplo n.º 3
0
        public bool CanShapeTogether(ShapeableTextCharacters shapeableTextCharacters)
        {
            if (!Text.Buffer.Equals(shapeableTextCharacters.Text.Buffer))
            {
                return(false);
            }

            if (Text.Start + Text.Length != shapeableTextCharacters.Text.Start)
            {
                return(false);
            }

            if (BidiLevel != shapeableTextCharacters.BidiLevel)
            {
                return(false);
            }

            if (!MathUtilities.AreClose(Properties.FontRenderingEmSize,
                                        shapeableTextCharacters.Properties.FontRenderingEmSize))
            {
                return(false);
            }

            if (Properties.Typeface != shapeableTextCharacters.Properties.Typeface)
            {
                return(false);
            }

            if (Properties.BaselineAlignment != shapeableTextCharacters.Properties.BaselineAlignment)
            {
                return(false);
            }

            return(true);
        }
Exemplo n.º 4
0
 public static bool operator ==(RelativeValue first, RelativeValue second)
 {
     if (first._reference == second._reference)
     {
         return(MathUtilities.AreClose(first._value, second._value));
     }
     return(false);
 }
Exemplo n.º 5
0
 public static bool operator !=(RelativeValue first, RelativeValue second)
 {
     if (!(first._reference != second._reference))
     {
         return(!MathUtilities.AreClose(first._value, second._value));
     }
     return(true);
 }
Exemplo n.º 6
0
 /// <summary>
 /// Determines whether a column can be resized by dragging the border of its header.  If star sizing
 /// is being used, there are special conditions that can prevent a column from being resized:
 /// 1. The column is the last visible column.
 /// 2. All columns are constrained by either their maximum or minimum values.
 /// </summary>
 /// <param name="column">Column to check.</param>
 /// <returns>Whether or not the column can be resized by dragging its header.</returns>
 private static bool CanResizeColumn(DataGridColumn column)
 {
     if (column.OwningGrid != null && column.OwningGrid.ColumnsInternal != null && column.OwningGrid.UsesStarSizing &&
         (column.OwningGrid.ColumnsInternal.LastVisibleColumn == column || !MathUtilities.AreClose(column.OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, column.OwningGrid.CellsWidth)))
     {
         return(false);
     }
     return(column.ActualCanUserResize);
 }
Exemplo n.º 7
0
        public IEnumerable <Rect> HitTestTextRange(int start, int length)
        {
            if (start + length <= 0)
            {
                return(Array.Empty <Rect>());
            }

            var result = new List <Rect>(TextLines.Count);

            var currentY = 0d;

            foreach (var textLine in TextLines)
            {
                //Current line isn't covered.
                if (textLine.FirstTextSourceIndex + textLine.Length < start)
                {
                    currentY += textLine.Height;

                    continue;
                }

                var textBounds = textLine.GetTextBounds(start, length);

                if (textBounds.Count > 0)
                {
                    foreach (var bounds in textBounds)
                    {
                        Rect?last = result.Count > 0 ? result[result.Count - 1] : null;

                        if (last.HasValue && MathUtilities.AreClose(last.Value.Right, bounds.Rectangle.Left) && MathUtilities.AreClose(last.Value.Top, currentY))
                        {
                            result[result.Count - 1] = last.Value.WithWidth(last.Value.Width + bounds.Rectangle.Width);
                        }
                        else
                        {
                            result.Add(bounds.Rectangle.WithY(currentY));
                        }

                        foreach (var runBounds in bounds.TextRunBounds)
                        {
                            start  += runBounds.Length;
                            length -= runBounds.Length;
                        }
                    }
                }

                if (textLine.FirstTextSourceIndex + textLine.Length >= start + length)
                {
                    break;
                }

                currentY += textLine.Height;
            }

            return(result);
        }
Exemplo n.º 8
0
        public void Two_Equivalent_Single_Values_Are_Close()
        {
            var expectedValue = (float)AnyValue;
            var actualValue   = (float)_calculatedAnyValue;

            var actual = MathUtilities.AreClose(expectedValue, actualValue);

            Assert.True(actual);
            Assert.Equal((float)Math.Round(expectedValue, 5), (float)Math.Round(actualValue, 4));
        }
        public override bool EvaluateLayout(DesignerView view, UIElement adorner)
        {
            BaseAdornerLayout.LayoutCache cache = BaseAdornerLayout.GetCache((DependencyObject)adorner);
            Matrix m1_1 = cache.ElementToDesignerViewTransformMatrix;
            Matrix m1_2 = cache.DesignerViewToViewportMatrix;
            bool   flag = base.EvaluateLayout(view, adorner);

            if (!MathUtilities.AreClose(m1_1, cache.ElementToDesignerViewTransformMatrix) || !MathUtilities.AreClose(m1_2, cache.DesignerViewToViewportMatrix))
            {
                this.SetupTransform(adorner);
            }
            return(flag);
        }
Exemplo n.º 10
0
            /// <summary>
            /// OnLayoutUpdated handler. Validates that all participating definitions
            /// have updated min size value. Forces another layout update cycle if needed.
            /// </summary>
            private void OnLayoutUpdated(object sender, EventArgs e)
            {
                double sharedMinSize = 0;

                //  accumulate min size of all participating definitions
                for (int i = 0, count = _registry.Count; i < count; ++i)
                {
                    sharedMinSize = Math.Max(sharedMinSize, _registry[i].MinSize);
                }

                bool sharedMinSizeChanged = !MathUtilities.AreClose(_minSize, sharedMinSize);

                //  compare accumulated min size with min sizes of the individual definitions
                for (int i = 0, count = _registry.Count; i < count; ++i)
                {
                    DefinitionBase definitionBase = _registry[i];

                    if (sharedMinSizeChanged || definitionBase.LayoutWasUpdated)
                    {
                        //  if definition's min size is different, then need to re-measure
                        if (!MathUtilities.AreClose(sharedMinSize, definitionBase.MinSize))
                        {
                            Grid parentGrid = (Grid)definitionBase.Parent;
                            parentGrid.InvalidateMeasure();
                            definitionBase.UseSharedMinimum = true;
                        }
                        else
                        {
                            definitionBase.UseSharedMinimum = false;

                            //  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 (!MathUtilities.AreClose(sharedMinSize, definitionBase.SizeCache))
                            {
                                Grid parentGrid = (Grid)definitionBase.Parent;
                                parentGrid.InvalidateArrange();
                            }
                        }

                        definitionBase.LayoutWasUpdated = false;
                    }
                }

                _minSize = sharedMinSize;

                _layoutUpdatedHost.LayoutUpdated -= _layoutUpdated;
                _layoutUpdatedHost = null;

                _broadcastInvalidation = true;
            }
Exemplo n.º 11
0
        public object Convert(IList <object> values, Type targetType, object parameter, CultureInfo culture)
        {
            if (parameter == null ||
                values == null ||
                values.Count != 4 ||
                !(values[0] is ScrollBarVisibility visiblity) ||
                !(values[1] is double offset) ||
                !(values[2] is double extent) ||
                !(values[3] is double viewport))
            {
                return(AvaloniaProperty.UnsetValue);
            }

            if (visiblity == ScrollBarVisibility.Auto)
            {
                if (extent == viewport)
                {
                    return(false);
                }

                double target;

                if (parameter is double d)
                {
                    target = d;
                }
                else if (parameter is string s)
                {
                    target = double.Parse(s, NumberFormatInfo.InvariantInfo);
                }
                else
                {
                    return(AvaloniaProperty.UnsetValue);
                }

                // Calculate the percent so that we can see if we are near the edge of the range
                double percent = MathUtilities.Clamp(offset * 100.0 / (extent - viewport), 0, 100);

                if (MathUtilities.AreClose(percent, target))
                {
                    // We are at the end of the range, so no need for this button to be shown
                    return(false);
                }

                return(true);
            }

            return(false);
        }
Exemplo n.º 12
0
        //TODO Animation
        internal void EnsureDetailsContentHeight()
        {
            if ((_detailsElement != null) &&
                (_detailsContent != null) &&
                (double.IsNaN(_detailsContent.Height)) &&
                (AreDetailsVisible) &&
                (!double.IsNaN(_detailsDesiredHeight)) &&
                !MathUtilities.AreClose(_detailsContent.Bounds.Inflate(_detailsContent.Margin).Height, _detailsDesiredHeight) &&
                Slot != -1)
            {
                _detailsDesiredHeight = _detailsContent.Bounds.Inflate(_detailsContent.Margin).Height;

                if (true)
                {
                    _detailsElement.ContentHeight = _detailsDesiredHeight;
                }
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Attempts to convert a DataGridLength instance to the given type.
        /// </summary>
        /// <param name="context">
        /// An ITypeDescriptorContext that provides a format context.
        /// </param>
        /// <param name="culture">
        /// The CultureInfo to use for the conversion.
        /// </param>
        /// <param name="value">The DataGridLength to convert.</param>
        /// <param name="destinationType">The type to which to convert the DataGridLength instance.</param>
        /// <returns>
        /// The object which was constructed.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// An ArgumentNullException is thrown if the example object is null.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// A NotSupportedException is thrown if the object is not null and is not a DataGridLength,
        /// or if the destinationType isn't one of the valid destination types.
        /// </exception>
        ///<SecurityNote>
        ///     Critical: calls InstanceDescriptor ctor which LinkDemands
        ///     PublicOK: can only make an InstanceDescriptor for DataGridLength, not an arbitrary class
        ///</SecurityNote>
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
            {
                throw new ArgumentNullException("destinationType");
            }
            if (destinationType != typeof(string))
            {
                throw DataGridError.DataGridLengthConverter.CannotConvertTo(destinationType.ToString());
            }
            DataGridLength?dataGridLength = value as DataGridLength?;

            if (!dataGridLength.HasValue)
            {
                throw DataGridError.DataGridLengthConverter.InvalidDataGridLength("value");
            }
            else
            {
                // Convert dataGridLength to a string
                switch (dataGridLength.Value.UnitType)
                {
                //  for Auto print out "Auto". value is always "1.0"
                case DataGridLengthUnitType.Auto:
                    return("Auto");

                case DataGridLengthUnitType.SizeToHeader:
                    return("SizeToHeader");

                case DataGridLengthUnitType.SizeToCells:
                    return("SizeToCells");

                //  Star has one special case when value is "1.0".
                //  in this case drop value part and print only "Star"
                case DataGridLengthUnitType.Star:
                    return(
                        MathUtilities.AreClose(1.0, dataGridLength.Value.Value)
                            ? _starSuffix
                            : Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture) + DataGridLengthConverter._starSuffix);

                default:
                    return(Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture));
                }
            }
        }
        public override void Arrange(UIElement adorner)
        {
            BaseAdornerLayout.LayoutCache cache = BaseAdornerLayout.GetCache((DependencyObject)adorner);
            Matrix m1_1 = cache.ElementToDesignerViewTransformMatrix;
            Matrix m1_2 = cache.DesignerViewToViewportMatrix;

            if (!MathUtilities.AreClose(m1_1, cache.ElementToDesignerViewTransformMatrix) || !MathUtilities.AreClose(m1_2, cache.DesignerViewToViewportMatrix))
            {
                this.SetupTransform(adorner);
            }
            Vector   scale = (Vector)adorner.GetValue(TransformAwareAdornerLayout.DesignerElementScalingFactorWithZoom);
            ViewItem view  = AdornerProperties.GetView((DependencyObject)adorner);

            this.SetAdornerBounds(adorner, view, new Point(0.0, 0.0), scale);
            if (!(view != (ViewItem)null) || cache.PlatformObjectHashCode == 0 || cache.PlatformObjectHashCode == view.PlatformObject.GetHashCode())
            {
                return;
            }
            cache.View                   = view;
            cache.RenderSize             = view.RenderSize;
            cache.PlatformObjectHashCode = view.PlatformObject.GetHashCode();
        }
        private void TimerTick(TimeSpan t)
        {

            // [<------------- normalizedTotalDur ------------------>]
            // [<---- Delay ---->][<---------- Duration ------------>]
            //                   ^- normalizedDelayEnd
            //                    [<----   normalizedInterpVal   --->]

            var normalizedInterpVal = 1d;

            if (!MathUtilities.AreClose(_duration.TotalSeconds, 0d))
            {
                var normalizedTotalDur = _delay + _duration;
                var normalizedDelayEnd = _delay.TotalSeconds / normalizedTotalDur.TotalSeconds;
                var normalizedPresentationTime = t.TotalSeconds / normalizedTotalDur.TotalSeconds;

                if (normalizedPresentationTime < normalizedDelayEnd
                    || MathUtilities.AreClose(normalizedPresentationTime, normalizedDelayEnd))
                {
                    normalizedInterpVal = 0d;
                }
                else
                {
                    normalizedInterpVal = (t.TotalSeconds - _delay.TotalSeconds) / _duration.TotalSeconds;
                }
            }

            // Clamp interpolation value.
            if (normalizedInterpVal >= 1d || normalizedInterpVal < 0d)
            {
                PublishNext(1d);
                PublishCompleted();
            }
            else
            {
                PublishNext(normalizedInterpVal);
            }
        }
Exemplo n.º 16
0
 public bool NearlyEquals(Vector other)
 {
     return(MathUtilities.AreClose(_x, other._x) &&
            MathUtilities.AreClose(_y, other._y));
 }
Exemplo n.º 17
0
        public IEnumerable<Rect> HitTestTextRange(int start, int length)
        {
            if (start + length <= 0)
            {
                return Array.Empty<Rect>();
            }

            var result = new List<Rect>(TextLines.Count);
            
            var currentY = 0d;
            var currentPosition = 0;
            var currentRect = Rect.Empty;

            foreach (var textLine in TextLines)
            {
                //Current line isn't covered.
                if (currentPosition + textLine.TextRange.Length <= start)
                {
                    currentY += textLine.Height;
                    currentPosition += textLine.TextRange.Length;

                    continue;
                }

                //The whole line is covered.
                if (currentPosition >= start && start + length > currentPosition + textLine.TextRange.Length)
                {
                    currentRect = new Rect(textLine.Start, currentY, textLine.WidthIncludingTrailingWhitespace,
                        textLine.Height);
                    
                    result.Add(currentRect);
                    
                    currentY += textLine.Height;
                    currentPosition += textLine.TextRange.Length;
                    
                    continue;
                }
                
                var startX = textLine.Start;
                
                //A portion of the line is covered.
                for (var index = 0; index < textLine.TextRuns.Count; index++)
                {
                    var currentRun = (ShapedTextCharacters)textLine.TextRuns[index];
                    ShapedTextCharacters? nextRun = null;

                    if (index + 1 < textLine.TextRuns.Count)
                    {
                        nextRun = (ShapedTextCharacters)textLine.TextRuns[index + 1];
                    }

                    if (nextRun != null)
                    {
                        if (nextRun.Text.Start < currentRun.Text.Start && start + length < currentRun.Text.End)
                        {
                            goto skip;
                        }

                        if (currentRun.Text.Start >= start + length)
                        {
                            goto skip;
                        }

                        if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < start)
                        {
                            goto skip;
                        }

                        if (currentRun.Text.End < start)
                        {
                            goto skip;
                        }
                        
                        goto noop;
                        
                        skip:
                        {
                            startX += currentRun.Size.Width;

                            currentPosition = currentRun.Text.Start;
                        }
                        
                        continue;
                        
                        noop:{ }
                    }

                    var endOffset = currentRun.GlyphRun.GetDistanceFromCharacterHit(
                        currentRun.ShapedBuffer.IsLeftToRight ?
                            new CharacterHit(start + length) :
                            new CharacterHit(start));

                    var endX = startX + endOffset;
                    
                    var startOffset = currentRun.GlyphRun.GetDistanceFromCharacterHit(
                        currentRun.ShapedBuffer.IsLeftToRight ?
                            new CharacterHit(start) :
                            new CharacterHit(start + length));

                    startX += startOffset;

                    var characterHit = currentRun.GlyphRun.IsLeftToRight ?
                        currentRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) :
                        currentRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);
                    
                    currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;

                    if(nextRun != null)
                    {
                        if (currentRun.ShapedBuffer.IsLeftToRight == nextRun.ShapedBuffer.IsLeftToRight)
                        {
                            endOffset = nextRun.GlyphRun.GetDistanceFromCharacterHit(
                                nextRun.ShapedBuffer.IsLeftToRight ?
                                    new CharacterHit(start + length) :
                                    new CharacterHit(start));
                            
                            index++;

                            endX += endOffset;

                            currentRun = nextRun;

                            if (currentRun.ShapedBuffer.IsLeftToRight)
                            {
                                characterHit = nextRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);

                                currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
                            }
                        }
                    }

                    if (endX < startX)
                    {
                        (endX, startX) = (startX, endX);
                    }

                    var width = endX - startX;

                    if (result.Count > 0 && MathUtilities.AreClose(currentRect.Top, currentY) &&
                        MathUtilities.AreClose(currentRect.Right, startX))
                    {
                        result[result.Count - 1] = currentRect.WithWidth(currentRect.Width + width);
                    }
                    else
                    {
                        currentRect = new Rect(startX, currentY, width, textLine.Height);
                        
                        result.Add(currentRect);
                    }
                    
                    if (currentRun.ShapedBuffer.IsLeftToRight)
                    {
                        if (nextRun != null)
                        {
                            if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= start + length)
                            {
                                break;
                            }
                          
                            currentPosition = nextRun.Text.End;
                        }
                        else
                        {
                            if (currentPosition >= start + length)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        if (currentPosition <= start)
                        {
                            break;
                        }
                    }

                    if (!currentRun.ShapedBuffer.IsLeftToRight && currentPosition != currentRun.Text.Start)
                    {
                        endX += currentRun.GlyphRun.Size.Width - endOffset;
                    }

                    startX = endX;
                }

                if (currentPosition == start || currentPosition == start + length)
                {
                    break;
                }
                
                if (textLine.TextRange.Start + textLine.TextRange.Length >= start + length)
                {
                    break;
                }

                currentY += textLine.Height;
            }

            return result;
        }
Exemplo n.º 18
0
        private static void CheckAndInvalidateAdorner(DesignerView view, ViewItem element, UIElement adorner)
        {
            if (view.Context == null)
            {
                return;
            }
            Matrix m1 = TransformUtil.GetTransformToImmediateParent((DependencyObject)view).Value;
            Matrix identity;
            Size   s1;

            if (element != (ViewItem)null)
            {
                identity = TransformUtil.GetSelectionFrameTransformToDesignerView(view.Context, element).Value;
                s1       = element.RenderSize;
            }
            else
            {
                identity = Matrix.Identity;
                s1       = Size.Empty;
            }
            BaseAdornerLayout.LayoutCache cache = BaseAdornerLayout.GetCache((DependencyObject)adorner);
            bool flag1 = false;
            bool flag2 = false;
            bool flag3 = false;

            if (element != (ViewItem)null && !MathUtilities.AreClose(s1, cache.RenderSize))
            {
                flag1 = true;
            }
            if (element != (ViewItem)null && !MathUtilities.AreClose(identity, cache.ElementToDesignerViewTransformMatrix))
            {
                flag1 = true;
                flag2 = true;
            }
            if (!MathUtilities.AreClose(m1, cache.DesignerViewToViewportMatrix))
            {
                flag1 = true;
                flag2 = true;
            }
            if (element != (ViewItem)null && !element.IsVisible)
            {
                ViewItem view1 = view.Context.Services.GetRequiredService <ModelService>().Root.View;
                for (ViewItem viewItem = element; viewItem != view1 && viewItem != (ViewItem)null; viewItem = viewItem.VisualParent)
                {
                    if (viewItem.Visibility == Visibility.Collapsed)
                    {
                        flag3 = true;
                        break;
                    }
                }
            }
            if (flag3)
            {
                object obj = adorner.ReadLocalValue(UIElement.VisibilityProperty);
                if (adorner.ReadLocalValue(BaseAdornerLayout.CachedVisibilityProperty) == DependencyProperty.UnsetValue)
                {
                    if (obj != DependencyProperty.UnsetValue)
                    {
                        adorner.SetValue(BaseAdornerLayout.CachedVisibilityProperty, obj);
                    }
                    else
                    {
                        adorner.SetValue(BaseAdornerLayout.CachedVisibilityProperty, (object)Visibility.Visible);
                    }
                    adorner.Visibility = Visibility.Collapsed;
                }
            }
            else
            {
                object obj = adorner.ReadLocalValue(BaseAdornerLayout.CachedVisibilityProperty);
                if (obj != DependencyProperty.UnsetValue)
                {
                    adorner.SetValue(UIElement.VisibilityProperty, obj);
                }
                adorner.ClearValue(BaseAdornerLayout.CachedVisibilityProperty);
            }
            if (!flag1 && !flag2)
            {
                return;
            }
            if (element != (ViewItem)null)
            {
                cache.RenderSize = s1;
                cache.ElementToDesignerViewTransformMatrix = identity;
                cache.PlatformObjectHashCode = element.PlatformObject.GetHashCode();
            }
            cache.DesignerViewToViewportMatrix = m1;
            cache.DesignerView = view;
            UIElement uiElement = VisualTreeHelper.GetParent((DependencyObject)adorner) as UIElement;

            if (flag1)
            {
                adorner.InvalidateMeasure();
                if (uiElement != null)
                {
                    uiElement.InvalidateMeasure();
                }
            }
            if (!flag2)
            {
                return;
            }
            adorner.InvalidateVisual();
            if (uiElement == null)
            {
                return;
            }
            uiElement.InvalidateVisual();
        }
Exemplo n.º 19
0
        private IReadOnlyList <TextBounds> GetTextBoundsRightToLeft(int firstTextSourceIndex, int textLength)
        {
            var characterIndex = firstTextSourceIndex + textLength;

            var result           = new List <TextBounds>(TextRuns.Count);
            var lastDirection    = FlowDirection.LeftToRight;
            var currentDirection = lastDirection;

            var currentPosition = FirstTextSourceIndex;
            var remainingLength = textLength;

            var    startX       = Start + WidthIncludingTrailingWhitespace;
            double currentWidth = 0;
            var    currentRect  = Rect.Empty;

            for (var index = TextRuns.Count - 1; index >= 0; index--)
            {
                if (TextRuns[index] is not DrawableTextRun currentRun)
                {
                    continue;
                }

                if (currentPosition + currentRun.TextSourceLength <= firstTextSourceIndex)
                {
                    startX -= currentRun.Size.Width;

                    currentPosition += currentRun.TextSourceLength;

                    continue;
                }

                var characterLength = 0;
                var endX            = startX;

                if (currentRun is ShapedTextCharacters currentShapedRun)
                {
                    var offset = Math.Max(0, firstTextSourceIndex - currentPosition);

                    currentPosition += offset;

                    var startIndex = currentRun.Text.Start + offset;

                    var endOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
                        currentShapedRun.ShapedBuffer.IsLeftToRight ?
                        new CharacterHit(startIndex + remainingLength) :
                        new CharacterHit(startIndex));

                    endX += endOffset - currentShapedRun.Size.Width;

                    var startOffset = currentShapedRun.GlyphRun.GetDistanceFromCharacterHit(
                        currentShapedRun.ShapedBuffer.IsLeftToRight ?
                        new CharacterHit(startIndex) :
                        new CharacterHit(startIndex + remainingLength));

                    startX += startOffset - currentShapedRun.Size.Width;

                    var endHit   = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);
                    var startHit = currentShapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);

                    characterLength = Math.Abs(startHit.FirstCharacterIndex + startHit.TrailingLength - endHit.FirstCharacterIndex - endHit.TrailingLength);

                    currentDirection = currentShapedRun.ShapedBuffer.IsLeftToRight ?
                                       FlowDirection.LeftToRight :
                                       FlowDirection.RightToLeft;
                }
                else
                {
                    if (currentPosition + currentRun.TextSourceLength <= characterIndex)
                    {
                        endX -= currentRun.Size.Width;
                    }

                    if (currentPosition < firstTextSourceIndex)
                    {
                        startX -= currentRun.Size.Width;

                        characterLength = currentRun.TextSourceLength;
                    }
                }

                if (endX < startX)
                {
                    (endX, startX) = (startX, endX);
                }

                //Lines that only contain a linebreak need to be covered here
                if (characterLength == 0)
                {
                    characterLength = NewLineLength;
                }

                var runWidth         = endX - startX;
                var currentRunBounds = new TextRunBounds(new Rect(startX, 0, runWidth, Height), currentPosition, characterLength, currentRun);

                if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
                {
                    currentRect = currentRect.WithWidth(currentWidth + runWidth);

                    var textBounds = result[result.Count - 1];

                    textBounds.Rectangle = currentRect;

                    textBounds.TextRunBounds.Add(currentRunBounds);
                }
                else
                {
                    currentRect = currentRunBounds.Rectangle;

                    result.Add(new TextBounds(currentRect, currentDirection, new List <TextRunBounds> {
                        currentRunBounds
                    }));
                }

                currentWidth    += runWidth;
                currentPosition += characterLength;

                if (currentDirection == FlowDirection.LeftToRight)
                {
                    if (currentPosition > characterIndex)
                    {
                        break;
                    }
                }
                else
                {
                    if (currentPosition <= firstTextSourceIndex)
                    {
                        break;
                    }
                }

                lastDirection    = currentDirection;
                remainingLength -= characterLength;

                if (remainingLength <= 0)
                {
                    break;
                }
            }

            return(result);
        }
Exemplo n.º 20
0
            /// <summary>
            /// OnLayoutUpdated handler. Validates that all participating definitions
            /// have updated min size value. Forces another layout update cycle if needed.
            /// </summary>
            private void OnLayoutUpdated(object sender, EventArgs e)
            {
                double sharedMinSize = 0;

                //  accumulate min size of all participating definitions
                for (int i = 0, count = _registry.Count; i < count; ++i)
                {
                    sharedMinSize = Math.Max(sharedMinSize, _registry[i].MinSize);
                }

                bool sharedMinSizeChanged = !MathUtilities.AreClose(_minSize, sharedMinSize);

                //  compare accumulated min size with min sizes of the individual definitions
                for (int i = 0, count = _registry.Count; i < count; ++i)
                {
                    DefinitionBase definitionBase = _registry[i];

                    // 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 = !MathUtilities.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)
                    {
                        // 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)
                    {
                        // d was a short-pole, and still is.  measure is valid
                        // iff the shared size didn't change
                        measureIsValid = !sharedMinSizeChanged;
                    }
                    else
                    {
                        // 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 &&
                                          MathUtilities.GreaterThanOrClose(definitionBase._minSize, this.MinSize));
                    }

                    if (!measureIsValid)
                    {
                        definitionBase.Parent.InvalidateMeasure();
                    }
                    else if (!MathUtilities.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
                        definitionBase.Parent.InvalidateArrange();
                    }

                    // now we can restore the invariant, and clear the layout flag
                    definitionBase.UseSharedMinimum = useSharedMinimum;
                    definitionBase.LayoutWasUpdated = false;
                }

                _minSize = sharedMinSize;

                _layoutUpdatedHost.LayoutUpdated -= _layoutUpdated;
                _layoutUpdatedHost = null;

                _broadcastInvalidation = true;
            }
Exemplo n.º 21
0
        public override IReadOnlyList <TextBounds> GetTextBounds(int firstTextSourceCharacterIndex, int textLength)
        {
            if (firstTextSourceCharacterIndex + textLength <= FirstTextSourceIndex)
            {
                return(Array.Empty <TextBounds>());
            }

            var result           = new List <TextBounds>(TextRuns.Count);
            var lastDirection    = _flowDirection;
            var currentDirection = lastDirection;
            var currentPosition  = 0;
            var currentRect      = Rect.Empty;
            var startX           = Start;

            //A portion of the line is covered.
            for (var index = 0; index < TextRuns.Count; index++)
            {
                var currentRun = TextRuns[index] as DrawableTextRun;

                if (currentRun is null)
                {
                    continue;
                }

                TextRun?nextRun = null;

                if (index + 1 < TextRuns.Count)
                {
                    nextRun = TextRuns[index + 1];
                }

                if (nextRun != null)
                {
                    if (nextRun.Text.Start < currentRun.Text.Start && firstTextSourceCharacterIndex + textLength < currentRun.Text.End)
                    {
                        goto skip;
                    }

                    if (currentRun.Text.Start >= firstTextSourceCharacterIndex + textLength)
                    {
                        goto skip;
                    }

                    if (currentRun.Text.Start > nextRun.Text.Start && currentRun.Text.Start < firstTextSourceCharacterIndex)
                    {
                        goto skip;
                    }

                    if (currentRun.Text.End < firstTextSourceCharacterIndex)
                    {
                        goto skip;
                    }

                    goto noop;

skip:
                    {
                        startX += currentRun.Size.Width;
                    }

                    continue;

noop:
                    {
                    }
                }


                var endX      = startX;
                var endOffset = 0d;

                switch (currentRun)
                {
                case ShapedTextCharacters shapedRun:
                {
                    endOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit(
                        shapedRun.ShapedBuffer.IsLeftToRight ?
                        new CharacterHit(firstTextSourceCharacterIndex + textLength) :
                        new CharacterHit(firstTextSourceCharacterIndex));

                    endX += endOffset;

                    var startOffset = shapedRun.GlyphRun.GetDistanceFromCharacterHit(
                        shapedRun.ShapedBuffer.IsLeftToRight ?
                        new CharacterHit(firstTextSourceCharacterIndex) :
                        new CharacterHit(firstTextSourceCharacterIndex + textLength));

                    startX += startOffset;

                    var characterHit = shapedRun.GlyphRun.IsLeftToRight ?
                                       shapedRun.GlyphRun.GetCharacterHitFromDistance(endOffset, out _) :
                                       shapedRun.GlyphRun.GetCharacterHitFromDistance(startOffset, out _);

                    currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;

                    currentDirection = shapedRun.ShapedBuffer.IsLeftToRight ?
                                       FlowDirection.LeftToRight :
                                       FlowDirection.RightToLeft;

                    if (nextRun is ShapedTextCharacters nextShaped)
                    {
                        if (shapedRun.ShapedBuffer.IsLeftToRight == nextShaped.ShapedBuffer.IsLeftToRight)
                        {
                            endOffset = nextShaped.GlyphRun.GetDistanceFromCharacterHit(
                                nextShaped.ShapedBuffer.IsLeftToRight ?
                                new CharacterHit(firstTextSourceCharacterIndex + textLength) :
                                new CharacterHit(firstTextSourceCharacterIndex));

                            index++;

                            endX += endOffset;

                            currentRun = nextShaped;

                            if (nextShaped.ShapedBuffer.IsLeftToRight)
                            {
                                characterHit = nextShaped.GlyphRun.GetCharacterHitFromDistance(endOffset, out _);

                                currentPosition = characterHit.FirstCharacterIndex + characterHit.TrailingLength;
                            }
                        }
                    }

                    break;
                }

                default:
                {
                    if (firstTextSourceCharacterIndex + textLength >= currentRun.Text.Start + currentRun.Text.Length)
                    {
                        endX += currentRun.Size.Width;
                    }

                    break;
                }
                }

                if (endX < startX)
                {
                    (endX, startX) = (startX, endX);
                }

                var width = endX - startX;

                if (lastDirection == currentDirection && result.Count > 0 && MathUtilities.AreClose(currentRect.Right, startX))
                {
                    var textBounds = new TextBounds(currentRect.WithWidth(currentRect.Width + width), currentDirection);

                    result[result.Count - 1] = textBounds;
                }
                else
                {
                    currentRect = new Rect(startX, 0, width, Height);

                    result.Add(new TextBounds(currentRect, currentDirection));
                }

                if (currentDirection == FlowDirection.LeftToRight)
                {
                    if (nextRun != null)
                    {
                        if (nextRun.Text.Start > currentRun.Text.Start && nextRun.Text.Start >= firstTextSourceCharacterIndex + textLength)
                        {
                            break;
                        }

                        currentPosition = nextRun.Text.End;
                    }
                    else
                    {
                        if (currentPosition >= firstTextSourceCharacterIndex + textLength)
                        {
                            break;
                        }
                    }
                }
                else
                {
                    if (currentPosition <= firstTextSourceCharacterIndex)
                    {
                        break;
                    }

                    if (currentPosition != currentRun.Text.Start)
                    {
                        endX += currentRun.Size.Width - endOffset;
                    }
                }

                lastDirection = currentDirection;
                startX        = endX;
            }

            return(result);
        }
Exemplo n.º 22
0
 /// <summary>
 /// Returns a boolean indicating whether the size is equal to the other given size (numerically).
 /// </summary>
 /// <param name="other">The other size to test equality against.</param>
 /// <returns>True if this size is equal to other; False otherwise.</returns>
 public bool NearlyEquals(Size other)
 {
     return(MathUtilities.AreClose(_width, other._width) &&
            MathUtilities.AreClose(_height, other._height));
 }