Exemplo n.º 1
0
 internal LineRecord(int offset, TextBoxLine line)
 {
     _offset = offset;
     _length = line.Length;
     _contentLength = line.ContentLength;
     _width = line.Width;
 }
Exemplo n.º 2
0
        // Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete.
        // Formats the line preceding the first directly affected line after a TextContainer change.
        // In general this line might grow as content in the following line is absorbed.
        private void FormatFirstIncrementalLine(int lineIndex, double constraintWidth, LineProperties lineProperties, TextBoxLine line,
            out int lineOffset, out bool endOfParagraph)
        {
            int originalEndOffset = _lineMetrics[lineIndex].EndOffset;
            lineOffset = _lineMetrics[lineIndex].Offset;

            using (line)
            {
                line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter);

                _lineMetrics[lineIndex] = new LineRecord(lineOffset, line);

                lineOffset += line.Length;
                endOfParagraph = line.EndOfParagraph;
            }

            // Don't clear the cached Visual unless something changed.
            if (originalEndOffset != _lineMetrics[lineIndex].EndOffset)
            {
                ClearLineVisual(lineIndex);
            }
        }
Exemplo n.º 3
0
        // Helper for IncrementalMeasureLinesAfterInsert, IncrementalMeasureLinesAfterDelete.
        // Formats line until we hit a synchronization point, a position where we know
        // following lines could not be affected by the change.
        private void SyncLineMetrics(DirtyTextRange range, double constraintWidth, LineProperties lineProperties, TextBoxLine line,
            bool endOfParagraph, int lineIndex, int lineOffset)
        {
            bool offsetSyncOk = (range.PositionsAdded == 0 || range.PositionsRemoved == 0);
            int lastCoveredCharOffset = range.StartIndex + Math.Max(range.PositionsAdded, range.PositionsRemoved);

            // Keep updating lines until we find a synchronized position.
            while (!endOfParagraph &&
                   (lineIndex == _lineMetrics.Count ||
                    !offsetSyncOk ||
                    lineOffset != _lineMetrics[lineIndex].Offset))
            {
                if (lineIndex < _lineMetrics.Count &&
                    lineOffset >= _lineMetrics[lineIndex].EndOffset)
                {
                    // If the current line offset starts past the current line metric offset,
                    // remove the metric.  This happens when the previous line
                    // frees up enough space to completely consume the following line.
                    // We can't simply replace the record without potentially missing our
                    // [....] position.
                    _lineMetrics.RemoveAt(lineIndex); // 
                    RemoveLineVisualRange(lineIndex, 1);
                }
                else
                {
                    using (line)
                    {
                        line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter);

                        LineRecord record = new LineRecord(lineOffset, line);

                        if (lineIndex == _lineMetrics.Count ||
                            lineOffset + line.Length <= _lineMetrics[lineIndex].Offset)
                        {
                            // The new line preceeds the old line, insert a new record.

                            // 
                            _lineMetrics.Insert(lineIndex, record);
                            AddLineVisualPlaceholder(lineIndex);
                        }
                        else
                        {
                            // We expect to be colliding with the old line directly.
                            // If we extend past it, we're in danger of needlessly
                            // re-formatting the entire doc (ie, we miss the real
                            // [....] position and don't stop until EndOfParagraph).
                            Invariant.Assert(lineOffset < _lineMetrics[lineIndex].EndOffset);

                            _lineMetrics[lineIndex] = record;
                            ClearLineVisual(lineIndex);

                            // If this line ends past the invalidated region, and it
                            // has a hard line break, it's safe to synchronize on the next
                            // line metric with a matching start offset.
                            offsetSyncOk |= lastCoveredCharOffset <= record.EndOffset && line.HasLineBreak;
                        }

                        lineIndex++;
                        lineOffset += line.Length;
                        endOfParagraph = line.EndOfParagraph;
                    }
                }
            }

            // Remove any trailing lines that got absorbed into the new last line.
            if (endOfParagraph && lineIndex < _lineMetrics.Count)
            {
                int count = _lineMetrics.Count - lineIndex;
                _lineMetrics.RemoveRange(lineIndex, count);
                RemoveLineVisualRange(lineIndex, count);
            }
        }
Exemplo n.º 4
0
        // Measures content invalidated due to a TextContainer change.
        private void IncrementalMeasureLinesAfterDelete(double constraintWidth, LineProperties lineProperties, DirtyTextRange range, ref Size desiredSize)
        {
            int delta = range.PositionsAdded - range.PositionsRemoved;
            Invariant.Assert(delta < 0);

            int firstLineIndex = GetLineIndexFromOffset(range.StartIndex);

            // Clip the scope of the affected lines to the region of the document
            // we've already inspected.  Clipping happens when background layout
            // has not yet completed but an incremental update happens.
            int endOffset = range.StartIndex + -delta - 1;
            if (endOffset > _lineMetrics[_lineMetrics.Count - 1].EndOffset)
            {
                Invariant.Assert(this.IsBackgroundLayoutPending);
                endOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset;
                if (range.StartIndex == endOffset)
                {
                    // Nothing left to do until background layout runs.
                    return;
                }
            }

            int lastLineIndex = GetLineIndexFromOffset(endOffset);

            // Increment the offsets of all following lines.
            // 
            for (int i = lastLineIndex + 1; i < _lineMetrics.Count; i++)
            {
                _lineMetrics[i].Offset += delta;
            }

            TextBoxLine line = new TextBoxLine(this);
            int lineIndex = firstLineIndex;
            int lineOffset;
            bool endOfParagraph;

            // We need to re-format the previous line, because if someone inserted
            // a hard break, the first directly affected line might now be shorter
            // and mergeable with its predecessor.
            if (lineIndex > 0) // 
            {
                FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph);
            }
            else
            {
                lineOffset = _lineMetrics[lineIndex].Offset;
                endOfParagraph = false;
            }

            // 



            // Update the first affected line.  If it's completely covered, remove it entirely below.
            if (!endOfParagraph &&
                (range.StartIndex > lineOffset || range.StartIndex + -delta < _lineMetrics[lineIndex].EndOffset))
            {
                // Only part of the line is covered, reformat it.
                using (line)
                {
                    line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter);

                    _lineMetrics[lineIndex] = new LineRecord(lineOffset, line);

                    lineOffset += line.Length;
                    endOfParagraph = line.EndOfParagraph;
                }
                ClearLineVisual(lineIndex);
                lineIndex++;
            }

            // Remove all the following lines that are completely covered.
            // 
            _lineMetrics.RemoveRange(lineIndex, lastLineIndex - lineIndex + 1);
            RemoveLineVisualRange(lineIndex, lastLineIndex - lineIndex + 1);

            // Recalc the following lines not directly affected as needed.
            SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset);

            desiredSize = BruteForceCalculateDesiredSize();
        }
Exemplo n.º 5
0
        // Measures content invalidated due to a TextContainer change.
        private void IncrementalMeasureLinesAfterInsert(double constraintWidth, LineProperties lineProperties, DirtyTextRange range, ref Size desiredSize)
        {
            int delta = range.PositionsAdded - range.PositionsRemoved;
            Invariant.Assert(delta >= 0);
            
            int lineIndex = GetLineIndexFromOffset(range.StartIndex, LogicalDirection.Forward);

            if (delta > 0)
            {
                // Increment of the offsets of all following lines.
                // 
                for (int i = lineIndex + 1; i < _lineMetrics.Count; i++)
                {
                    _lineMetrics[i].Offset += delta;
                }
            }

            TextBoxLine line = new TextBoxLine(this);
            int lineOffset;
            bool endOfParagraph = false;

            // We need to re-format the previous line, because if someone inserted
            // a hard break, the first directly affected line might now be shorter
            // and mergeable with its predecessor.
            if (lineIndex > 0) // 
            {
                FormatFirstIncrementalLine(lineIndex - 1, constraintWidth, lineProperties, line, out lineOffset, out endOfParagraph);
            }
            else
            {
                lineOffset = _lineMetrics[lineIndex].Offset;
            }

            // Format the line directly affected by the change.
            // If endOfParagraph == true, then the line was absorbed into its
            // predessor (because its new content is thinner, or because the
            // TextWrapping property changed).
            if (!endOfParagraph)
            {
                using (line)
                {
                    line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter);

                    _lineMetrics[lineIndex] = new LineRecord(lineOffset, line);

                    lineOffset += line.Length;
                    endOfParagraph = line.EndOfParagraph;
                }
                ClearLineVisual(lineIndex);
                lineIndex++;
            }

            // Recalc the following lines not directly affected as needed.
            SyncLineMetrics(range, constraintWidth, lineProperties, line, endOfParagraph, lineIndex, lineOffset);

            desiredSize = BruteForceCalculateDesiredSize();
        }
Exemplo n.º 6
0
        // Performs one iteration of background measure.
        // Background measure always works at the end of the current
        // line metrics array -- invalidations to prevoiusly examined
        // content is handled by incremental layout, synchronously.
        //
        // Returns the full content size, omitting any unanalyzed content
        // at the document end.
        private Size FullMeasureTick(double constraintWidth, LineProperties lineProperties)
        {
            Size desiredSize;
            TextBoxLine line = new TextBoxLine(this);
            int lineOffset;
            bool endOfParagraph;

            // Find the next position for this iteration.

            if (_lineMetrics.Count == 0)
            {
                desiredSize = new Size();
                lineOffset = 0;
            }
            else
            {
                desiredSize = _contentSize;
                lineOffset = _lineMetrics[_lineMetrics.Count - 1].EndOffset;
            }

            // Calculate a stop time.
            // We limit work to just a few milliseconds per iteration
            // to avoid blocking the thread.
            DateTime stopTime;

            if ((ScrollBarVisibility)((Control)_host).GetValue(ScrollViewer.VerticalScrollBarVisibilityProperty) == ScrollBarVisibility.Auto)
            {
                // Workaround for bug 1766924.
                // When VerticalScrollBarVisiblity == Auto, there's a problem with
                // our interaction with ScrollViewer.  Disable background layout to
                // mitigate the problem until we can take a real fix in v.next.
                // 
                stopTime = DateTime.MaxValue;
            }
            else
            {
                stopTime = DateTime.Now.AddMilliseconds(_maxMeasureTimeMs);
            }

            // Format lines until we hit the end of document or run out of time.

            do
            {
                using (line)
                {
                    line.Format(lineOffset, constraintWidth, constraintWidth, lineProperties, _cache.TextRunCache, _cache.TextFormatter);

                    // This is a loop invariant, but has negligable cost.
                    // 
                    _lineHeight = lineProperties.CalcLineAdvance(line.Height);

                    _lineMetrics.Add(new LineRecord(lineOffset, line));

                    // Desired width is always max of calculated line widths. 
                    // Desired height is sum of all line heights.
                    desiredSize.Width = Math.Max(desiredSize.Width, line.Width);
                    desiredSize.Height += _lineHeight;

                    lineOffset += line.Length;
                    endOfParagraph = line.EndOfParagraph;
                }
            }
            while (!endOfParagraph && DateTime.Now < stopTime);

            if (!endOfParagraph)
            {
                // Ran out of time.  Defer to background layout.
                SetFlags(true, Flags.BackgroundLayoutPending);
                this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(OnBackgroundMeasure), null);
            }
            else
            {
                // Finished the entire document.  Stop background layout.
                SetFlags(false, Flags.BackgroundLayoutPending);
            }

            return desiredSize;
        }
Exemplo n.º 7
0
        // Updates line visuals on an ArrangeOverride call.
        private void ArrangeVisuals(Size arrangeSize)
        {
            Invariant.Assert(_dirtyList == null); // We should never see pending incremental updates during arrange.

            //
            // Initialize state.
            //

            if (_visualChildren == null)
            {
                _visualChildren = new List<TextBoxLineDrawingVisual>(1) ;
            }

            EnsureCache();

            LineProperties lineProperties = _cache.LineProperties;
            TextBoxLine line = new TextBoxLine(this);

            //
            // Calculate the current viewport extent, in lines.
            // We won't do any work for lines that aren't visible.
            //

            int firstLineIndex;
            int lastLineIndex;

            GetVisibleLines(out firstLineIndex, out lastLineIndex);

            SetViewportLines(firstLineIndex, lastLineIndex);

            double width = GetWrappingWidth(arrangeSize.Width);

            double horizontalOffset = GetTextAlignmentCorrection(lineProperties.TextAlignment, width);
            double verticalOffset = this.VerticalAlignmentOffset;

            if (_scrollData != null)
            {
                horizontalOffset -= _scrollData.HorizontalOffset;
                verticalOffset -= _scrollData.VerticalOffset;
            }

            // Remove invalidated lines from the visual tree.
            DetachDiscardedVisualChildren();

            //
            // Iterate across the visible lines.
            // If we have a cached visual, simply update its current offset.
            // Otherwise, allocate and render a new visual.
            //

            double formatWidth = GetWrappingWidth(_previousConstraint.Width);

            for (int lineIndex = firstLineIndex; lineIndex <= lastLineIndex; lineIndex++)
            {
                TextBoxLineDrawingVisual lineVisual = GetLineVisual(lineIndex);

                if (lineVisual == null)
                {
                    LineRecord metrics = _lineMetrics[lineIndex];

                    using (line)
                    {
                        line.Format(metrics.Offset, formatWidth, width, lineProperties, _cache.TextRunCache, _cache.TextFormatter);
                        
                        // We should be in [....] with current metrics, unless background layout is pending.
                        if (!this.IsBackgroundLayoutPending)
                        {
                            Invariant.Assert(metrics.Length == line.Length, "Line is out of [....] with metrics!");
                        }

                        lineVisual = line.CreateVisual();
                    }

                    SetLineVisual(lineIndex, lineVisual);
                    AttachVisualChild(lineVisual);
                }

                lineVisual.Offset = new Vector(horizontalOffset, verticalOffset + lineIndex * _lineHeight);
            }
        }
Exemplo n.º 8
0
        // Returns a formatted TextBoxLine at the specified index.
        // Caller must Dispose the TextBoxLine.
        // This method is expensive.
        private TextBoxLine GetFormattedLine(int lineIndex, out LineProperties lineProperties)
        {
            TextBoxLine line = new TextBoxLine(this);
            LineRecord metrics = _lineMetrics[lineIndex];
            lineProperties = GetLineProperties();

            Control hostControl = (Control)_host;
            TextFormattingMode textFormattingMode = TextOptions.GetTextFormattingMode(hostControl);
            TextFormatter formatter = TextFormatter.FromCurrentDispatcher(textFormattingMode);

            double width = GetWrappingWidth(this.RenderSize.Width);
            double formatWidth = GetWrappingWidth(_previousConstraint.Width);

            line.Format(metrics.Offset, formatWidth, width, lineProperties, new TextRunCache(), formatter);
            Invariant.Assert(metrics.Length == line.Length, "Line is out of [....] with metrics!");

            return line;
        }