Exemple #1
0
        /// <summary>
        /// Redo the layout of the paragraph given the Source changes indicated in the given details.
        /// </summary>
        /// <param name="details"></param>
        private void Relayout(SourceChangeDetails details)
        {
            // We would prefer to keep both sources intact, but we can't just do Source = details.NewSource,
            // because there is currently no way to change the Source of existing segments we may want to reuse.
            Source.Copyfrom(details.NewSource);
            var oldHeight = Height;
            var oldWidth  = Width;

            using (var gh = Root.Site.DrawingInfo)
            {
                // Enhance JohnT: margins: need to adjust MaxWidth for margins and padding of containing boxes.
                var info = new LayoutInfo(ChildTransformFromRootTransform(gh.Transform), Root.LastLayoutInfo.MaxWidth,
                                          gh.VwGraphics, Root.LastLayoutInfo.RendererFactory);
                var builder = new ParaBuilder(this, info);
                using (var lcb = new LayoutCallbacks(Root))
                {
                    builder.Relayout(details, lcb);
                    if (Height != oldHeight || Width != oldWidth)
                    {
                        RelayoutParents(gh);
                    }
                }
            }
        }
Exemple #2
0
		/// <summary>
		/// Redo layout. Should produce the same segments as FullLayout, but assume that segments for text up to
		/// details.StartChange may be reused (if not affected by changing line breaks), and segments after
		/// details.StartChange+details.DeleteCount may be re-used if a line break works out (and after adjusting
		/// their begin offset).
		/// </summary>
		/// <param name="details"></param>
		internal void Relayout(SourceChangeDetails details, LayoutCallbacks lcb)
		{
			m_reuseableLines = m_para.Lines;
			m_lines = new List<ParaLine>();
			m_renderRunIndex = 0;
			m_ichRendered = 0;
			IRenderRun last = m_renderRuns[m_renderRuns.Count - 1];
			m_ichLim = last.RenderStart + last.RenderLength;
			m_lastRenderRunIndex = m_renderRuns.Count;
			Rectangle invalidateRect = m_para.InvalidateRect;
			int delta = details.InsertCount - details.DeleteCount;
			int oldHeight = m_para.Height;
			int oldWidth = m_para.Width;
			// Make use of details.StartChange to reuse some lines at start.
			if (m_reuseableLines.Count > 0)
			{
				// As long as we have two complete lines before the change, we can certainly reuse the first of them.
				while (m_reuseableLines.Count > 2 && details.StartChange > m_reuseableLines[2].IchMin)
				{
					m_lines.Add(m_reuseableLines[0]);
					m_reuseableLines.RemoveAt(0);
				}
				// If we still have one complete line before the change, we can reuse it provided there is white
				// space after the end of the line and before the change.
				if (m_reuseableLines.Count > 1)
				{
					int startNextLine = m_reuseableLines[1].IchMin;
					if (details.StartChange > startNextLine)
					{
						bool fGotWhite = false;
						string line1Text = m_reuseableLines[1].CheckedText;
						int lim = details.StartChange - startNextLine;
						var cpe = LgIcuCharPropEngineClass.Create();
						for (int ich = 0; ich < lim; ich++)
						{
							// Enhance JohnT: possibly we need to consider surrogates here?
							// Worst case is we don't reuse a line we could have, since a surrogate won't
							// be considered white.
							if (cpe.get_IsSeparator(Convert.ToInt32(line1Text[ich])))
							{
								fGotWhite = true;
								break;
							}
						}
						if (fGotWhite)
						{
							m_lines.Add(m_reuseableLines[0]);
							m_reuseableLines.RemoveAt(0);
						}
					}
				}
				m_ichRendered = m_reuseableLines[0].IchMin;
				int topOfFirstDiscardedLine = m_reuseableLines[0].Top;
				// We don't need to invalidate the lines we're keeping.
				invalidateRect = new Rectangle(invalidateRect.Left, invalidateRect.Top + topOfFirstDiscardedLine,
											   invalidateRect.Width, invalidateRect.Height - topOfFirstDiscardedLine);
			}

			// Figure out which run we need to continue from, to correspond to the start of the first line
			// we need to rebuild.
			while (m_renderRunIndex < m_renderRuns.Count && m_renderRuns[m_renderRunIndex].RenderLim <= m_ichRendered)
				m_renderRunIndex++;

			while (!Finished)
			{
				// Todo: I think we need to adjust available width if this is the first line.
				BuildALine();
				// Drop any initial reusable lines we now determine to be unuseable after all.
				// If we've used characters beyond the start of this potentially reusable line, we can't reuse it.
				// Also, we don't reuse empty lines. Typically an empty line is left over from a previously empty
				// paragraph, and we no longer need the empty segment, even though it doesn't have any of the same
				// characters (since it has none) as the segment that has replaced it.
				while (m_reuseableLines.Count > 0 && (m_ichRendered > m_reuseableLines[0].IchMin + delta || m_reuseableLines[0].Length == 0))
				{
					m_reuseableLines.RemoveAt(0);
				}
				if (m_reuseableLines.Count > 0)
				{
					// See if we can resync.
					var nextLine = m_reuseableLines[0];
					if (m_ichRendered == nextLine.IchMin + delta)
					{
						// reuse it.
						int top = m_gapTop;
						if (m_lines.Count > 0)
						{
							ParaLine previous = m_lines.Last();
							previous.LastBox.Next = nextLine.FirstBox;
							top = TopOfNextLine(previous, nextLine.Ascent);
						}

						m_lines.AddRange(m_reuseableLines);
						if (top != nextLine.Top)
						{
							ParaLine previous = null;
							foreach (var line in m_reuseableLines)
							{
								if (previous != null) // first time top has already been computed
									top = TopOfNextLine(previous, line.Ascent);
								line.Top = top; // BEFORE ArrangeBoxes, since it gets copied to the individual boxes
								m_currentLine.ArrangeBoxes(m_para.Style.ParaAlignment, m_gapLeft, m_gapRight, 0, m_layoutInfo.MaxWidth, TopDepth);
								previous = line;
							}
						}
						else
						{
							// reusable lines have not moved, we don't need to invalidate them.
							invalidateRect.Height -= (m_reuseableLines.Last().Bottom - top);
						}
						for (Box box = nextLine.FirstBox; box != null; box = box.Next)
						{
							if (box is StringBox)
								(box as StringBox).IchMin += delta;
						}

						break;
					}
				}

			}
			SetParaInfo();
			// if the paragraph got larger, we need to invalidate the extra area.
			// (But, don't reduce it if it got smaller; we want to invalidate all the old stuff as well as all the new.)
			if (m_para.Height > oldHeight)
				invalidateRect.Height += m_para.Height - oldHeight;
			if (m_para.Width > oldWidth)
				invalidateRect.Width += m_para.Width - oldWidth;
			lcb.InvalidateInRoot(invalidateRect);
		}
Exemple #3
0
		/// <summary>
		/// Redo the layout of the paragraph given the Source changes indicated in the given details.
		/// </summary>
		/// <param name="details"></param>
		private void Relayout(SourceChangeDetails details)
		{
			// We would prefer to keep both sources intact, but we can't just do Source = details.NewSource,
			// because there is currently no way to change the Source of existing segments we may want to reuse.
			Source.Copyfrom(details.NewSource);
			var oldHeight = Height;
			var oldWidth = Width;
			using (var gh = Root.Site.DrawingInfo)
			{
				// Enhance JohnT: margins: need to adjust MaxWidth for margins and padding of containing boxes.
				var info = new LayoutInfo(ChildTransformFromRootTransform(gh.Transform), Root.LastLayoutInfo.MaxWidth,
					gh.VwGraphics, Root.LastLayoutInfo.RendererFactory);
				var builder = new ParaBuilder(this, info);
				using (var lcb = new LayoutCallbacks(Root))
				{
					builder.Relayout(details, lcb);
					if (Height != oldHeight || Width != oldWidth)
						RelayoutParents(gh);
				}
			}
		}
Exemple #4
0
        /// <summary>
        /// Redo layout. Should produce the same segments as FullLayout, but assume that segments for text up to
        /// details.StartChange may be reused (if not affected by changing line breaks), and segments after
        /// details.StartChange+details.DeleteCount may be re-used if a line break works out (and after adjusting
        /// their begin offset).
        /// </summary>
        /// <param name="details"></param>
        internal void Relayout(SourceChangeDetails details, LayoutCallbacks lcb)
        {
            m_reuseableLines = m_para.Lines;
            m_lines          = new List <ParaLine>();
            m_renderRunIndex = 0;
            m_ichRendered    = 0;
            IRenderRun last = m_renderRuns[m_renderRuns.Count - 1];

            m_ichLim             = last.RenderStart + last.RenderLength;
            m_lastRenderRunIndex = m_renderRuns.Count;
            Rectangle invalidateRect = m_para.InvalidateRect;
            int       delta          = details.InsertCount - details.DeleteCount;
            int       oldHeight      = m_para.Height;
            int       oldWidth       = m_para.Width;

            // Make use of details.StartChange to reuse some lines at start.
            if (m_reuseableLines.Count > 0)
            {
                // As long as we have two complete lines before the change, we can certainly reuse the first of them.
                while (m_reuseableLines.Count > 2 && details.StartChange > m_reuseableLines[2].IchMin)
                {
                    m_lines.Add(m_reuseableLines[0]);
                    m_reuseableLines.RemoveAt(0);
                }
                // If we still have one complete line before the change, we can reuse it provided there is white
                // space after the end of the line and before the change.
                if (m_reuseableLines.Count > 1)
                {
                    int startNextLine = m_reuseableLines[1].IchMin;
                    if (details.StartChange > startNextLine)
                    {
                        bool   fGotWhite = false;
                        string line1Text = m_reuseableLines[1].CheckedText;
                        int    lim       = details.StartChange - startNextLine;
                        var    cpe       = LgIcuCharPropEngineClass.Create();
                        for (int ich = 0; ich < lim; ich++)
                        {
                            // Enhance JohnT: possibly we need to consider surrogates here?
                            // Worst case is we don't reuse a line we could have, since a surrogate won't
                            // be considered white.
                            if (cpe.get_IsSeparator(Convert.ToInt32(line1Text[ich])))
                            {
                                fGotWhite = true;
                                break;
                            }
                        }
                        if (fGotWhite)
                        {
                            m_lines.Add(m_reuseableLines[0]);
                            m_reuseableLines.RemoveAt(0);
                        }
                    }
                }
                m_ichRendered = m_reuseableLines[0].IchMin;
                int topOfFirstDiscardedLine = m_reuseableLines[0].Top;
                // We don't need to invalidate the lines we're keeping.
                invalidateRect = new Rectangle(invalidateRect.Left, invalidateRect.Top + topOfFirstDiscardedLine,
                                               invalidateRect.Width, invalidateRect.Height - topOfFirstDiscardedLine);
            }

            // Figure out which run we need to continue from, to correspond to the start of the first line
            // we need to rebuild.
            while (m_renderRunIndex < m_renderRuns.Count && m_renderRuns[m_renderRunIndex].RenderLim <= m_ichRendered)
            {
                m_renderRunIndex++;
            }

            while (!Finished)
            {
                // Todo: I think we need to adjust available width if this is the first line.
                BuildALine();
                // Drop any initial reusable lines we now determine to be unuseable after all.
                // If we've used characters beyond the start of this potentially reusable line, we can't reuse it.
                // Also, we don't reuse empty lines. Typically an empty line is left over from a previously empty
                // paragraph, and we no longer need the empty segment, even though it doesn't have any of the same
                // characters (since it has none) as the segment that has replaced it.
                while (m_reuseableLines.Count > 0 && (m_ichRendered > m_reuseableLines[0].IchMin + delta || m_reuseableLines[0].Length == 0))
                {
                    m_reuseableLines.RemoveAt(0);
                }
                if (m_reuseableLines.Count > 0)
                {
                    // See if we can resync.
                    var nextLine = m_reuseableLines[0];
                    if (m_ichRendered == nextLine.IchMin + delta)
                    {
                        // reuse it.
                        int top = m_gapTop;
                        if (m_lines.Count > 0)
                        {
                            ParaLine previous = m_lines.Last();
                            previous.LastBox.Next = nextLine.FirstBox;
                            top = TopOfNextLine(previous, nextLine.Ascent);
                        }

                        m_lines.AddRange(m_reuseableLines);
                        if (top != nextLine.Top)
                        {
                            ParaLine previous = null;
                            foreach (var line in m_reuseableLines)
                            {
                                if (previous != null)                                 // first time top has already been computed
                                {
                                    top = TopOfNextLine(previous, line.Ascent);
                                }
                                line.Top = top;                                 // BEFORE ArrangeBoxes, since it gets copied to the individual boxes
                                m_currentLine.ArrangeBoxes(m_para.Style.ParaAlignment, m_gapLeft, m_gapRight, 0, m_layoutInfo.MaxWidth, TopDepth);
                                previous = line;
                            }
                        }
                        else
                        {
                            // reusable lines have not moved, we don't need to invalidate them.
                            invalidateRect.Height -= (m_reuseableLines.Last().Bottom - top);
                        }
                        for (Box box = nextLine.FirstBox; box != null; box = box.Next)
                        {
                            if (box is StringBox)
                            {
                                (box as StringBox).IchMin += delta;
                            }
                        }

                        break;
                    }
                }
            }
            SetParaInfo();
            // if the paragraph got larger, we need to invalidate the extra area.
            // (But, don't reduce it if it got smaller; we want to invalidate all the old stuff as well as all the new.)
            if (m_para.Height > oldHeight)
            {
                invalidateRect.Height += m_para.Height - oldHeight;
            }
            if (m_para.Width > oldWidth)
            {
                invalidateRect.Width += m_para.Width - oldWidth;
            }
            lcb.InvalidateInRoot(invalidateRect);
        }