public static float LineWidth(this Paragraph para, int lineIndex) { if (para.Breaks.Count == 0) { return(para.Glyphs.Sum(gx => gx.Width)); } BreakList.Node n = para.Breaks.Head.Skip(lineIndex - 1); if (n == null) { return(0); } GlyphList.Node g = para.Glyphs.Head; int idx = n.Value; g = g.Skip(idx); n = n.Next; float w = 0; while (g != null && (n == null || idx++ < n.Value)) { w += g.Value.Width; g = g.Next; } return(w); }
public static IEnumerable <float> LineWidths(this Paragraph para) { if (para.Breaks.Count == 0) { yield return(para.Glyphs.Sum(g => g.Width)); } else { BreakList.Node n = para.Breaks.Head; GlyphList.Node g = para.Glyphs.Head; float w = 0; int i = 0; while (n != null) { while (g != null && ++i <= n.Value) { w += g.Value.Width; g = g.Next; } i--; yield return(w); n = n.Next; w = 0; } while (g != null) { w += g.Value.Width; g = g.Next; } yield return(w); } }
public void MoveEndOfWord() { GlyphList.Node n = CurrentParagraph.Glyphs.Head.Skip(CursorPosition); while (n != null && !Paragraph.IsSpaceWrapCharacter(n.Value.Char)) { n = n.Next; CursorPosition++; } }
// used for cursor placement public static ParagraphPosition PositionAtLineWidth(this Paragraph para, float width, int line) { if (para.Glyphs == null || para.Glyphs.Count == 0 || line < 0 || line >= para.LineCount) { return(ParagraphPosition.Empty); } GlyphList.Node ng = para.Glyphs.Head; int breakIndex = -1; BreakList.Node nb = para.Breaks.Head; if (nb != null) { breakIndex = nb.Value; } int i = 0; float w = 0; float whalf = (int)(ng.Value.Width / 2f + 0.5f); int x = 0; int y = 0; while ((y < line || (w + whalf <= width)) && ng.Next != null) { if (i == breakIndex - 1) { if (y >= line) { return(new ParagraphPosition(i, x, y, w.Ceil(), ng.Value.Width)); } w = 0; whalf = (int)(ng.Value.Width / 2f + 0.5f); x = 0; y++; nb = nb.Next; if (nb != null) { breakIndex = nb.Value; } } else { x++; w += ng.Value.Width; whalf = (int)(ng.Value.Width / 2f + 0.5f); } ng = ng.Next; i++; } return(new ParagraphPosition(i, x, y, w.Ceil(), ng.Value.Width)); }
public void MoveNextWord() { bool loopFirst = true; if (CursorPosition >= CurrentParagraph.Length - 1) { if (CurrentParagraphIndex < Paragraphs.Count - 1) { MoveNextChar(); while (CurrentParagraph.Length <= 1) { MoveNextChar(); } loopFirst = false; } } GlyphList.Node n = CurrentParagraph.Glyphs.Head; int k = CursorPosition; if (loopFirst) { for (int i = 0; i < CursorPosition; i++) { n = n.Next; } k = CursorPosition; while (n != null && n.Value.Char != ' ') { n = n.Next; k++; } } while (n != null && n.Value.Char == ' ') { n = n.Next; k++; } if (k < CurrentParagraph.Length - 1) { CursorPosition = k; } else { CursorPosition = CurrentParagraph.Length - 1; } ResetCursorColoumns(); }
public virtual void MovePrevWord() { if (CursorPosition == 0) { if (CurrentParagraphIndex > 0) { MovePrevChar(); while (CurrentParagraph.Length <= 1) { MovePrevChar(); } if (CursorPosition > 0) { CursorPosition--; } while (CurrentParagraph.Glyphs [CursorPosition].Char == ' ' && CursorPosition > 0) { CursorPosition--; } CursorPosition++; } ResetCursorColoumns(); return; } GlyphList.Node n = CurrentParagraph.Glyphs.Head; int i = 0; int k = 0; bool spaceFlag = false; while (k != CursorPosition && n != null) { if (n.Value.Char == ' ') { spaceFlag = true; } else if (spaceFlag) { i = k; spaceFlag = false; } n = n.Next; k++; } CursorPosition = i; ResetCursorColoumns(); }
// used for general purpose public static ParagraphPosition PositionAtIndex(this Paragraph para, int pos) { if (para.Glyphs == null || para.Glyphs.Count == 0 || pos <= 0) { return(ParagraphPosition.Empty); } pos = pos.Clamp(0, para.Glyphs.Count - 1); GlyphList.Node ng = para.Glyphs.Head; int breakIndex = -1; BreakList.Node nb = para.Breaks.Head; if (nb != null) { breakIndex = nb.Value; } int i = 0; int x = 0; int w = 0; int y = 0; while (i < pos && ng.Next != null) { if (i == breakIndex - 1) { w = 0; x = 0; y++; nb = nb.Next; if (nb != null) { breakIndex = nb.Value; } } else { x++; w += ng.Value.Width; } i++; ng = ng.Next; } return(new ParagraphPosition(pos, x, y, w, ng.Value.Width)); }
public void InsertLineBreak() { if (CursorPosition >= CurrentParagraph.Length - 1) { if (CurrentParagraphIndex >= Paragraphs.Count - 1) { // most common case: Writing at the edge Paragraphs.AddLast(new Paragraph(Paragraphs.Count, BreakWidth, String.Empty, Font, Flags)); MoveNextChar(); } else { MoveNextChar(); Paragraph current = CurrentParagraph; Paragraph para = new Paragraph(CurrentParagraphIndex, BreakWidth, "\n", Font, Flags); para.LineOffset = current.LineOffset; para.Height = LineHeight; Paragraphs.Insert(CurrentParagraphIndex, para); //current = CurrentParagraph; } } else if (CursorPosition == 0) { Paragraph current = CurrentParagraph; Paragraph para = new Paragraph(CurrentParagraphIndex, BreakWidth, "\n", Font, Flags); para.LineOffset = current.LineOffset; para.Height = LineHeight; para.Top = current.Top; Paragraphs.Insert(CurrentParagraphIndex, para); CurrentParagraphIndex++; } else { Paragraph para = new Paragraph(CurrentParagraphIndex + 1, BreakWidth); Paragraph cp = CurrentParagraph; GlyphList.Node np = cp.Glyphs.Head; for (int i = 0; i < CursorPosition - 1; i++) { np = np.Next; } GlyphList.Node npTail = np; np = np.Next; while (np != null) { para.Glyphs.AddLast(np.Value); np = np.Next; } para.NeedsWordWrap = true; // cut np.. npTail.Next = null; cp.Glyphs.ResetCurrent(); cp.AppendChar('\n', Font, Flags); Paragraphs.Insert(++CurrentParagraphIndex, para); } CurrentParagraphIndex = CurrentParagraphIndex.Clamp(0, Paragraphs.Count - 1); CursorPosition = 0; ResetCursorColoumns(); //Paragraphs.OnUpdateAsync (CurrentParagraphIndex - 1, BreakWidth); Paragraphs.OnUpdate(CurrentParagraphIndex - 1, BreakWidth, false, 250); }
public void WordWrap(float breakWidth) { // ToDo: Still not perfect: // * Lines with ending NewLine may hang over // (the non-printable NewLine character is right above breakwidth) // * Lines without any break-chars may hang one character over breakwidth lock (SyncObject) { if (!NeedsWordWrap && BreakWidth == breakWidth) { return; } BreakWidth = breakWidth; NeedsWordWrap = false; // Since here, we completely work with temporary local variables // and set final values once finished. GlyphList.Node curr = Glyphs.Head; GlyphList.Node prev = curr; BreakList lineBreaks = new BreakList(); float currentWidth = 0; float totalWidth = 0; float maxWidth = 0; int pos = 0; int prevBreakCharPos = 0; while (curr != null) { currentWidth += curr.Value.Width; totalWidth += currentWidth; if (currentWidth > BreakWidth && curr.Next != null) { if (prevBreakCharPos == 0) { lineBreaks.AddLast(pos); currentWidth = curr.Value.Width; prev = curr; } else { lineBreaks.AddLast(prevBreakCharPos); prevBreakCharPos = 0; currentWidth = 0; while (prev != curr) { currentWidth += prev.Value.Width; prev = prev.Next; } } } else if (IsSpaceWrapCharacter(curr.Value.Char) || (IsWrapCharacter(curr.Value.Char) && curr.Next != null && !IsSpaceWrapCharacter(curr.Next.Value.Char))) { prevBreakCharPos = pos + 1; prev = curr; } maxWidth = Math.Max(maxWidth, currentWidth); curr = curr.Next; pos++; } // Update final values Concurrency.LockFreeUpdate(ref m_Breaks, lineBreaks); Width = maxWidth.Ceil(); TotalGlyphWidth = totalWidth; } }