Beispiel #1
0
 internal void FullLayout()
 {
     m_lines              = new List <ParaLine>();
     m_renderRunIndex     = 0;
     m_ichRendered        = 0;
     m_lastRenderRunIndex = m_renderRuns.Count;
     if (m_renderRuns.Count != 0)
     {
         IRenderRun last = m_renderRuns[m_renderRuns.Count - 1];
         m_ichLim = last.RenderStart + last.RenderLength;
         while (!Finished)
         {
             BuildALine();
         }
     }
     else
     {
         m_ichLim = 0;
     }
     SetParaInfo();
 }
Beispiel #2
0
		/// <summary>
		/// Add something to the line. Return true if we should keep trying to add more. (That is, all of the current thing fit,
		/// and there is still room left; this routine is not responsible to determine whether there IS anything else to add.)
		/// If there is not yet anything in the line, this routine MUST add something; otherwise, it is allowed to fail,
		/// returning false without changing anything.
		/// </summary>
		private bool AddSomethingToLine()
		{
			m_currentRenderRun = m_renderRuns[m_renderRunIndex];
			Box boxToAdd = m_currentRenderRun.Box;
			if (boxToAdd != null)
			{
				// The current run works out to a single box; add it if it fits. Add it anyway if the line
				// is currently empty.
				boxToAdd.Layout(m_layoutInfo);
				if (m_currentLine.FirstBox != null && boxToAdd.Width > m_spaceLeftOnCurrentLine)
					return false;
				m_spaceLeftOnCurrentLine -= boxToAdd.Width;
				AddBoxToLine(boxToAdd, LgEndSegmentType.kestOkayBreak); // always OK to break after non-string.
				boxToAdd.Container = m_para;
				m_ichRendered = m_currentRenderRun.RenderLim;
				m_renderRunIndex++;
			}
			else
			{
				// current run is not a simple box. Make a text box out of part or all of it, or possibly also subsequent
				// runs that are not boxes and use the same renderer.
				IRenderEngine renderer = m_layoutInfo.GetRenderer(m_currentRenderRun.Ws);
				// If our text source doesn't yet know about the writing system factory, make sure it does.
				if (m_para.Source.GetWsFactory() == null)
					m_para.Source.SetWsFactory(renderer.WritingSystemFactory);
				int ichRunLim = GetLimitOfRunWithSameRenderer(renderer, m_renderRunIndex);
				ILgSegment seg;
				int dichLim, dxWidth;
				LgEndSegmentType est;
				bool mustGetSomeSeg = m_currentLine.FirstBox == null;
				var twsh = GetNextTwsh();

				renderer.FindBreakPoint(m_layoutInfo.VwGraphics, m_para.Source, null, m_ichRendered,
					ichRunLim, ichRunLim, false, mustGetSomeSeg, m_spaceLeftOnCurrentLine, (mustGetSomeSeg ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak),
										m_currentLine.FirstBox == null ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak, twsh,
										false, out seg, out dichLim, out dxWidth, out est, null);
				if (est == LgEndSegmentType.kestNothingFit && twsh != LgTrailingWsHandling.ktwshAll)
				{
					// Nothing of the one we were trying for, try for the other.
					twsh = GetNextTwsh();
					renderer.FindBreakPoint(m_layoutInfo.VwGraphics, m_para.Source, null, m_ichRendered,
						ichRunLim, ichRunLim, false, mustGetSomeSeg, m_spaceLeftOnCurrentLine, (mustGetSomeSeg ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak),
											m_currentLine.FirstBox == null ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak, twsh,
										false, out seg, out dichLim, out dxWidth, out est, null);
				}
				switch (est)
				{
					case LgEndSegmentType.kestNoMore:
					case LgEndSegmentType.kestOkayBreak:
					case LgEndSegmentType.kestMoreLines:
					case LgEndSegmentType.kestWsBreak:
					case LgEndSegmentType.kestMoreWhtsp:
						boxToAdd = new StringBox(m_para.Style, seg, m_ichRendered);
						boxToAdd.Layout(m_layoutInfo);
						m_spaceLeftOnCurrentLine -= boxToAdd.Width;
						m_ichRendered += dichLim;
						boxToAdd.Container = m_para;
						// If we get NoMore, we should also check, because it might really be a ws break at the end
						// of the run.
						if (est == LgEndSegmentType.kestNoMore && ichRunLim < m_para.Source.Length && ichRunLim > 0
							&& WsInSource(ichRunLim - 1, m_para.Source) != WsInSource(ichRunLim, m_para.Source))
						{
							// The renderer failed to detect it because not told to look further in the source,
							// but there is in fact a writing system break.
							// However, if the next character is a box, we want to treat it as a definitely good break.
							if (m_para.Source.IsThereABoxAt(ichRunLim))
								est = LgEndSegmentType.kestOkayBreak;
							else
								est = LgEndSegmentType.kestWsBreak;
						}
						AddBoxToLine(boxToAdd, est);
						m_renderRunIndex = AdvanceRenderRunIndexToIch(m_ichRendered, m_renderRunIndex);
						// We want to return true if more could be put on this line.
						// Of the cases that take this branch, NoMore means no more input at all, so we can't add any more;
						// MoreLines means this line is full. So for both of those we return false. OkayBreak allows us to try
						// to put more segments on this line, as does ws break, and moreWhtsp.
						return est == LgEndSegmentType.kestOkayBreak || est == LgEndSegmentType.kestWsBreak || est == LgEndSegmentType.kestMoreWhtsp;
					case LgEndSegmentType.kestNothingFit:
						Debug.Assert(m_currentLine.FirstBox != null, "Making segment must not return kestNothingFit if line contains nothing");
						return false;
					//case LgEndSegmentType.kestHardBreak:
					//    if()
					//    return true;
					default:
						Debug.Assert(false);
						break;
				}
			}
			return true;
		}
Beispiel #3
0
 void VerifyRenderRun(IRenderRun run, int start, int length, string label)
 {
     Assert.AreEqual(start, run.RenderStart, label + " - start");
     Assert.AreEqual(length, run.RenderLength, label + " - length");
 }
Beispiel #4
0
		void VerifyRenderRun(IRenderRun run, int start, int length, string label)
		{
			Assert.AreEqual(start, run.RenderStart, label + " - start");
			Assert.AreEqual(length, run.RenderLength, label + " - length");
		}
Beispiel #5
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);
        }
Beispiel #6
0
        /// <summary>
        /// Add something to the line. Return true if we should keep trying to add more. (That is, all of the current thing fit,
        /// and there is still room left; this routine is not responsible to determine whether there IS anything else to add.)
        /// If there is not yet anything in the line, this routine MUST add something; otherwise, it is allowed to fail,
        /// returning false without changing anything.
        /// </summary>
        private bool AddSomethingToLine()
        {
            m_currentRenderRun = m_renderRuns[m_renderRunIndex];
            Box boxToAdd = m_currentRenderRun.Box;

            if (boxToAdd != null)
            {
                // The current run works out to a single box; add it if it fits. Add it anyway if the line
                // is currently empty.
                boxToAdd.Layout(m_layoutInfo);
                if (m_currentLine.FirstBox != null && boxToAdd.Width > m_spaceLeftOnCurrentLine)
                {
                    return(false);
                }
                m_spaceLeftOnCurrentLine -= boxToAdd.Width;
                AddBoxToLine(boxToAdd, LgEndSegmentType.kestOkayBreak);                 // always OK to break after non-string.
                boxToAdd.Container = m_para;
                m_ichRendered      = m_currentRenderRun.RenderLim;
                m_renderRunIndex++;
            }
            else
            {
                // current run is not a simple box. Make a text box out of part or all of it, or possibly also subsequent
                // runs that are not boxes and use the same renderer.
                IRenderEngine renderer = m_layoutInfo.GetRenderer(m_currentRenderRun.Ws);
                // If our text source doesn't yet know about the writing system factory, make sure it does.
                if (m_para.Source.GetWsFactory() == null)
                {
                    m_para.Source.SetWsFactory(renderer.WritingSystemFactory);
                }
                int              ichRunLim = GetLimitOfRunWithSameRenderer(renderer, m_renderRunIndex);
                ILgSegment       seg;
                int              dichLim, dxWidth;
                LgEndSegmentType est;
                bool             mustGetSomeSeg = m_currentLine.FirstBox == null;
                var              twsh           = GetNextTwsh();

                renderer.FindBreakPoint(m_layoutInfo.VwGraphics, m_para.Source, null, m_ichRendered,
                                        ichRunLim, ichRunLim, false, mustGetSomeSeg, m_spaceLeftOnCurrentLine, (mustGetSomeSeg ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak),
                                        m_currentLine.FirstBox == null ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak, twsh,
                                        false, out seg, out dichLim, out dxWidth, out est, null);
                if (est == LgEndSegmentType.kestNothingFit && twsh != LgTrailingWsHandling.ktwshAll)
                {
                    // Nothing of the one we were trying for, try for the other.
                    twsh = GetNextTwsh();
                    renderer.FindBreakPoint(m_layoutInfo.VwGraphics, m_para.Source, null, m_ichRendered,
                                            ichRunLim, ichRunLim, false, mustGetSomeSeg, m_spaceLeftOnCurrentLine, (mustGetSomeSeg ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak),
                                            m_currentLine.FirstBox == null ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak, twsh,
                                            false, out seg, out dichLim, out dxWidth, out est, null);
                }
                switch (est)
                {
                case LgEndSegmentType.kestNoMore:
                case LgEndSegmentType.kestOkayBreak:
                case LgEndSegmentType.kestMoreLines:
                case LgEndSegmentType.kestWsBreak:
                case LgEndSegmentType.kestMoreWhtsp:
                    boxToAdd = new StringBox(m_para.Style, seg, m_ichRendered);
                    boxToAdd.Layout(m_layoutInfo);
                    m_spaceLeftOnCurrentLine -= boxToAdd.Width;
                    m_ichRendered            += dichLim;
                    boxToAdd.Container        = m_para;
                    // If we get NoMore, we should also check, because it might really be a ws break at the end
                    // of the run.
                    if (est == LgEndSegmentType.kestNoMore && ichRunLim < m_para.Source.Length && ichRunLim > 0 &&
                        WsInSource(ichRunLim - 1, m_para.Source) != WsInSource(ichRunLim, m_para.Source))
                    {
                        // The renderer failed to detect it because not told to look further in the source,
                        // but there is in fact a writing system break.
                        // However, if the next character is a box, we want to treat it as a definitely good break.
                        if (m_para.Source.IsThereABoxAt(ichRunLim))
                        {
                            est = LgEndSegmentType.kestOkayBreak;
                        }
                        else
                        {
                            est = LgEndSegmentType.kestWsBreak;
                        }
                    }
                    AddBoxToLine(boxToAdd, est);
                    m_renderRunIndex = AdvanceRenderRunIndexToIch(m_ichRendered, m_renderRunIndex);
                    // We want to return true if more could be put on this line.
                    // Of the cases that take this branch, NoMore means no more input at all, so we can't add any more;
                    // MoreLines means this line is full. So for both of those we return false. OkayBreak allows us to try
                    // to put more segments on this line, as does ws break, and moreWhtsp.
                    return(est == LgEndSegmentType.kestOkayBreak || est == LgEndSegmentType.kestWsBreak || est == LgEndSegmentType.kestMoreWhtsp);

                case LgEndSegmentType.kestNothingFit:
                    Debug.Assert(m_currentLine.FirstBox != null, "Making segment must not return kestNothingFit if line contains nothing");
                    return(false);

                //case LgEndSegmentType.kestHardBreak:
                //    if()
                //    return true;
                default:
                    Debug.Assert(false);
                    break;
                }
            }
            return(true);
        }