예제 #1
0
        /// <summary>
        /// OnChildDesiredSizeChanged
        /// Called from FlowDocumentPage for IContentHost implementation
        /// 



        internal void OnChildDesiredSizeChanged(UIElement child)
        {
            if (_structuralCache != null && _structuralCache.IsFormattedOnce && !_structuralCache.ForceReformat)
            {
                // If executed during formatting process, delay invalidation.
                // This may happen during formatting when text host notifies its about
                // baseline changes.
                if (_structuralCache.IsFormattingInProgress)
                {
                    Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        new DispatcherOperationCallback(OnChildDesiredSizeChangedAsync), child);
                    return;
                }

                // Get start and end positions
                int childStartIndex = TextContainerHelper.GetCPFromEmbeddedObject(child, ElementEdge.BeforeStart);
                if (childStartIndex < 0)
                {
                    return;
                }

                TextPointer childStart = new TextPointer(_structuralCache.TextContainer.Start);
                childStart.MoveByOffset(childStartIndex);
                TextPointer childEnd = new TextPointer(childStart);
                childEnd.MoveByOffset(TextContainerHelper.EmbeddedObjectLength);

                // Create new DTR for changing UIElement and add it to DRTList.
                DirtyTextRange dtr = new DirtyTextRange(childStartIndex, TextContainerHelper.EmbeddedObjectLength, TextContainerHelper.EmbeddedObjectLength);
                _structuralCache.AddDirtyTextRange(dtr);

                // Notify formatter about content invalidation.
                if (_formatter != null)
                {
                    _formatter.OnContentInvalidated(true, childStart, childEnd);
                }
            }
        }
예제 #2
0
        /// <summary>
        /// Returns an ICollection of bounding rectangles for the given ContentElement
        /// </summary>
        /// <param name="child">
        /// Content element for which rectangles are required
        /// </param>
        /// <remarks>
        /// Looks at the ContentElement e line by line and gets rectangle bounds for each line
        /// </remarks>
        protected virtual ReadOnlyCollection<Rect> GetRectanglesCore(ContentElement child)
        {
            if (child == null)
            {
                throw new ArgumentNullException("child");
            }

            // If layout data is not updated we assume that we will not be able to find the element we need and throw excception
            if (!IsLayoutDataValid)
            {
                // return empty collection
                return new ReadOnlyCollection<Rect>(new List<Rect>(0));
            }

            // Line props may be invalid, even if Measure/Arrange is valid - rendering only props are changing.
            LineProperties lineProperties = GetLineProperties();

            // Check for complex content
            if (_complexContent == null || !(_complexContent.TextContainer is TextContainer))
            {
                // return empty collection
                return new ReadOnlyCollection<Rect>(new List<Rect>(0));
            }

            // First find the element start and end position
            TextPointer start = FindElementPosition((IInputElement)child);
            if (start == null)
            {
                return new ReadOnlyCollection<Rect>(new List<Rect>(0));
            }

            TextPointer end = null;
            if (child is TextElement)
            {
                end = new TextPointer(((TextElement)child).ElementEnd);
            }
            else if (child is FrameworkContentElement)
            {
                end = new TextPointer(start);
                end.MoveByOffset(+1);
            }

            if (end == null)
            {
                return new ReadOnlyCollection<Rect>(new List<Rect>(0));
            }

            int startOffset = _complexContent.TextContainer.Start.GetOffsetToPosition(start);
            int endOffset = _complexContent.TextContainer.Start.GetOffsetToPosition(end);

            int lineIndex = 0;
            int lineOffset = 0;
            double lineHeightOffset = 0;
            int lineCount = LineCount;
            while (startOffset >= (lineOffset + GetLine(lineIndex).Length) && lineIndex < lineCount)
            {
Debug.Assert(lineCount == LineCount);
                lineOffset += GetLine(lineIndex).Length;
                lineIndex++;
                lineHeightOffset += GetLine(lineIndex).Height;
            }
            Debug.Assert(lineIndex < lineCount);

            int lineStart = lineOffset;
            List<Rect> rectangles = new List<Rect>();
            double wrappingWidth = CalcWrappingWidth(RenderSize.Width);

            TextRunCache textRunCache = new TextRunCache();

            Vector contentOffset = CalcContentOffset(RenderSize, wrappingWidth);
            do
            {
Debug.Assert(lineCount == LineCount);
                // Check that line index never exceeds line count
                Debug.Assert(lineIndex < lineCount);

                // Create lines as long as they are spanned by the element
                LineMetrics lineMetrics = GetLine(lineIndex);

                Line line = CreateLine(lineProperties);

                using (line)
                {
                    // Check if paragraph ellipsis are rendered
                    bool ellipsis = ParagraphEllipsisShownOnLine(lineIndex, lineOffset);
                    line.Format(lineStart, wrappingWidth, GetLineProperties(lineIndex == 0, lineProperties), lineMetrics.TextLineBreak, textRunCache, ellipsis);

                    // Verify consistency of line formatting
                    // 
                    if (lineMetrics.Length == line.Length)
                    {
                        //MS.Internal.Invariant.Assert(lineMetrics.Length == line.Length, "Line length is out of [....]");
                        //Debug.Assert(DoubleUtil.AreClose(CalcLineAdvance(line.Height, lineProperties), lineMetrics.Height), "Line height is out of [....].");

                        int boundStart = (startOffset >= lineStart) ? startOffset : lineStart;
                        int boundEnd = (endOffset < lineStart + lineMetrics.Length) ? endOffset : lineStart + lineMetrics.Length;

                        double xOffset = contentOffset.X;
                        double yOffset = contentOffset.Y + lineHeightOffset;
                        List<Rect> lineBounds = line.GetRangeBounds(boundStart, boundEnd - boundStart, xOffset, yOffset);
                        Debug.Assert(lineBounds.Count > 0);
                        rectangles.AddRange(lineBounds);
                    }
                }

                lineStart += lineMetrics.Length;
                lineHeightOffset += lineMetrics.Height;
                lineIndex++;
            }
            while (endOffset > lineStart);

            // Rectangles collection must be non-null
            Invariant.Assert(rectangles != null);
            return new ReadOnlyCollection<Rect>(rectangles);
        }
예제 #3
0
        // ------------------------------------------------------------------
        // IContentHost Helpers
        // ------------------------------------------------------------------

        /// <summary>
        /// Searches for an element in the _complexContent.TextContainer. If the element is found, returns the
        /// position at which it is found. Otherwise returns null.
        /// </summary>
        /// <param name="e">
        /// Element to be found.
        /// </param>
        /// <remarks>
        /// We assume that this function is called from within text if the caller knows that _complexContent exists
        /// and contains a TextContainer. Hence we assert for this condition within the function
        /// </remarks>
        private TextPointer FindElementPosition(IInputElement e)
        {
            // Parameter validation
            Debug.Assert(e != null);

            // Validate that this function is only called when a TextContainer exists as complex content
            Debug.Assert(_complexContent.TextContainer is TextContainer);

            TextPointer position;

            // If e is a TextElement we can optimize by checking its TextContainer
            if (e is TextElement)
            {
                if ((e as TextElement).TextContainer == _complexContent.TextContainer)
                {
                    // Element found
                    position = new TextPointer((e as TextElement).ElementStart);
                    return position;
                }
            }

            // Else: search for e in the complex content
            position = new TextPointer((TextPointer)_complexContent.TextContainer.Start);
            while (position.CompareTo((TextPointer)_complexContent.TextContainer.End) < 0)
            {
                // Search each position in _complexContent.TextContainer for the element
                switch (position.GetPointerContext(LogicalDirection.Forward))
                {
                    case TextPointerContext.EmbeddedElement:
                        DependencyObject embeddedObject = position.GetAdjacentElement(LogicalDirection.Forward);
                        if (embeddedObject is ContentElement || embeddedObject is UIElement)
                        {
                            if (embeddedObject == e as ContentElement || embeddedObject == e as UIElement)
                            {
                                return position;
                            }
                        }
                        break;
                    default:
                          break;
                }
                position.MoveByOffset(+1);
            }

            // Reached end of complex content without finding the element
            return null;
        }
예제 #4
0
파일: TextPointer.cs 프로젝트: mind0n/hive
 /// <summary>
 /// Returns a TextPointer at a new position by a specified symbol
 /// count.
 /// </summary>
 /// <param name="offset">
 /// Number of symbols to advance.  offset may be negative, in which
 /// case the TextPointer is moved backwards.
 /// </param>
 /// <param name="direction">
 /// LogicalDirection desired for a returned TextPointer.
 /// </param>
 /// <returns>
 /// TextPointer located at requested position in case if requested position
 /// does exist, otherwize returns null. LogicalDirection of the TextPointer
 /// returned is as specified by a <paramref name="direction"/>.
 /// </returns>
 /// <remarks>
 /// <para>This method, like all other TextPointer methods, defines a symbol
 /// as one of:</para>
 /// <para>- 16 bit Unicode character.</para>
 /// <para>- opening or closing tag of a <see cref="TextElement"/>.</para>
 /// <para>- the whole <see cref="UIElement"/> as atomic embedded object.</para>
 /// <para>See examples in <seealso cref="TextPointer.GetPositionAtOffset(int)"/> method with one parameter.</para>
 /// </remarks>
 public TextPointer GetPositionAtOffset(int offset, LogicalDirection direction)
 {
     TextPointer position = new TextPointer(this, direction);
     int actualCount = position.MoveByOffset(offset);
     if (actualCount == offset)
     {
         position.Freeze();
         return position;
     }
     else
     {
         return null;
     }
 }