void ParagraphLayout::GetLineBounds(int32 lineIndex, float& x1, float& y1, float& x2, float& y2) { _ValidateLayout(); if (fGlyphInfos.CountItems() == 0) { _GetEmptyLayoutBounds(x1, y1, x2, y2); return; } if (lineIndex < 0) lineIndex = 0; if (lineIndex >= fLineInfos.CountItems()) lineIndex = fLineInfos.CountItems() - 1; const LineInfo& lineInfo = fLineInfos.ItemAt(lineIndex); int32 firstGlyphIndex = lineInfo.textOffset; int32 lastGlyphIndex; if (lineIndex < fLineInfos.CountItems() - 1) lastGlyphIndex = fLineInfos.ItemAt(lineIndex + 1).textOffset - 1; else lastGlyphIndex = fGlyphInfos.CountItems() - 1; const GlyphInfo& firstInfo = fGlyphInfos.ItemAtFast(firstGlyphIndex); const GlyphInfo& lastInfo = fGlyphInfos.ItemAtFast(lastGlyphIndex); x1 = firstInfo.x; y1 = lineInfo.y; x2 = lastInfo.x + lastInfo.width; y2 = lineInfo.y + lineInfo.height; }
void ParagraphLayout::GetTextBounds(int32 textOffset, float& x1, float& y1, float& x2, float& y2) { _ValidateLayout(); if (fGlyphInfos.CountItems() == 0) { _GetEmptyLayoutBounds(x1, y1, x2, y2); return; } if (textOffset >= fGlyphInfos.CountItems()) { const GlyphInfo& glyph = fGlyphInfos.LastItem(); const LineInfo& line = fLineInfos.ItemAt(glyph.lineIndex); x1 = glyph.x + glyph.width; x2 = x1; y1 = line.y; y2 = y1 + line.height; return; } if (textOffset < 0) textOffset = 0; const GlyphInfo& glyph = fGlyphInfos.ItemAtFast(textOffset); const LineInfo& line = fLineInfos.ItemAt(glyph.lineIndex); x1 = glyph.x; x2 = x1 + glyph.width; y1 = line.y; y2 = y1 + line.height; }
int32 TextDocumentLayout::_ParagraphLayoutIndexForOffset(int32& textOffset) { _ValidateLayout(); int32 paragraphs = fParagraphLayouts.CountItems(); for (int32 i = 0; i < paragraphs - 1; i++) { const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(i); int32 length = info.layout->CountGlyphs(); if (textOffset >= length) { textOffset -= length; continue; } return i; } if (paragraphs > 0) { const ParagraphLayoutInfo& info = fParagraphLayouts.LastItem(); // Return last paragraph if the textOffset is still within or // exactly behind the last valid offset in that paragraph. int32 length = info.layout->CountGlyphs(); if (textOffset <= length) return paragraphs - 1; } return -1; }
BTextControl::BTextControl(const char* name, const char* label, const char* text, BMessage* message, uint32 flags) : BControl(name, label, message, flags | B_FRAME_EVENTS) { _InitData(label, text); _ValidateLayout(); }
BTextControl::BTextControl(const char* label, const char* text, BMessage* message) : BControl(NULL, label, message, B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS) { _InitData(label, text); _ValidateLayout(); }
BTextControl::BTextControl(BRect frame, const char* name, const char* label, const char* text, BMessage* message, uint32 mask, uint32 flags) : BControl(frame, name, label, message, mask, flags | B_FRAME_EVENTS) { _InitData(label); _InitText(text); _ValidateLayout(); }
void ParagraphLayout::Draw(BView* view, const BPoint& offset) { _ValidateLayout(); int lineCount = fLineInfos.CountItems(); for (int i = 0; i < lineCount; i++) { const LineInfo& line = fLineInfos.ItemAtFast(i); _DrawLine(view, line); } }
int32 ParagraphLayout::FirstOffsetOnLine(int32 lineIndex) { _ValidateLayout(); if (lineIndex < 0) lineIndex = 0; if (lineIndex >= fLineInfos.CountItems()) lineIndex = fLineInfos.CountItems() - 1; return fLineInfos.ItemAt(lineIndex).textOffset; }
int32 ParagraphLayout::LastOffsetOnLine(int32 lineIndex) { _ValidateLayout(); if (lineIndex < 0) lineIndex = 0; if (lineIndex >= fLineInfos.CountItems() - 1) return CountGlyphs() - 1; return fLineInfos.ItemAt(lineIndex + 1).textOffset - 1; }
float TextDocumentLayout::Height() { _ValidateLayout(); float height = 0.0f; if (fParagraphLayouts.CountItems() > 0) { const ParagraphLayoutInfo& lastLayout = fParagraphLayouts.LastItem(); height = lastLayout.y + lastLayout.layout->Height(); } return height; }
float ParagraphLayout::Height() { _ValidateLayout(); float height = 0.0f; if (fLineInfos.CountItems() > 0) { const LineInfo& lastLine = fLineInfos.LastItem(); height = lastLine.y + lastLine.height; } return height; }
int32 TextDocumentLayout::CountLines() { _ValidateLayout(); int32 lineCount = 0; int32 count = fParagraphLayouts.CountItems(); for (int32 i = 0; i < count; i++) { const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(i); lineCount += info.layout->CountLines(); } return lineCount; }
void TextDocumentLayout::Draw(BView* view, const BPoint& offset, const BRect& updateRect) { _ValidateLayout(); int layoutCount = fParagraphLayouts.CountItems(); for (int i = 0; i < layoutCount; i++) { const ParagraphLayoutInfo& layout = fParagraphLayouts.ItemAtFast(i); BPoint location(offset.x, offset.y + layout.y); if (location.y > updateRect.bottom) break; if (location.y + layout.layout->Height() > updateRect.top) layout.layout->Draw(view, location); } }
int32 ParagraphLayout::LineIndexForOffset(int32 textOffset) { _ValidateLayout(); if (fGlyphInfos.CountItems() == 0) return 0; if (textOffset >= fGlyphInfos.CountItems()) { const GlyphInfo& glyph = fGlyphInfos.LastItem(); return glyph.lineIndex; } if (textOffset < 0) textOffset = 0; const GlyphInfo& glyph = fGlyphInfos.ItemAtFast(textOffset); return glyph.lineIndex; }
void ParagraphLayout::Draw(BView* view, const BPoint& offset) { _ValidateLayout(); int lineCount = fLineInfos.CountItems(); for (int i = 0; i < lineCount; i++) { const LineInfo& line = fLineInfos.ItemAtFast(i); _DrawLine(view, offset, line); } const Bullet& bullet = fParagraphStyle.Bullet(); if (bullet.Spacing() > 0.0f && bullet.String().Length() > 0) { // Draw bullet at offset view->SetHighColor(0, 0, 0, 255); BPoint bulletPos(offset); bulletPos.x += fParagraphStyle.FirstLineInset() + fParagraphStyle.LineInset(); bulletPos.y += fLineInfos.ItemAt(0).maxAscent; view->DrawString(bullet.String(), bulletPos); } }
int32 TextDocumentLayout::TextOffsetAt(float x, float y, bool& rightOfCenter) { _ValidateLayout(); int32 textOffset = 0; rightOfCenter = false; int32 paragraphs = fParagraphLayouts.CountItems(); for (int32 i = 0; i < paragraphs; i++) { const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(i); if (y > info.y + info.layout->Height()) { textOffset += info.layout->CountGlyphs(); continue; } textOffset += info.layout->TextOffsetAt(x, y - info.y, rightOfCenter); break; } return textOffset; }
int32 TextDocumentLayout::_ParagraphLayoutIndexForLineIndex(int32& lineIndex, int32& paragraphOffset) { _ValidateLayout(); paragraphOffset = 0; int32 paragraphs = fParagraphLayouts.CountItems(); for (int32 i = 0; i < paragraphs; i++) { const ParagraphLayoutInfo& info = fParagraphLayouts.ItemAtFast(i); int32 lineCount = info.layout->CountLines(); if (lineIndex >= lineCount) { lineIndex -= lineCount; paragraphOffset += info.layout->CountGlyphs(); continue; } return i; } return -1; }
int32 ParagraphLayout::TextOffsetAt(float x, float y, bool& rightOfCenter) { _ValidateLayout(); rightOfCenter = false; int32 lineCount = fLineInfos.CountItems(); if (fGlyphInfos.CountItems() == 0 || lineCount == 0 || fLineInfos.ItemAtFast(0).y > y) { // Above first line or empty text return 0; } int32 lineIndex = 0; if (floorf(fLineInfos.LastItem().y + fLineInfos.LastItem().height + 0.5) > y) { // TODO: Optimize, can binary search line here: for (; lineIndex < lineCount; lineIndex++) { const LineInfo& line = fLineInfos.ItemAtFast(lineIndex); float lineBottom = floorf(line.y + line.height + 0.5); if (lineBottom > y) break; } } else { lineIndex = lineCount - 1; } // Found line const LineInfo& line = fLineInfos.ItemAtFast(lineIndex); int32 textOffset = line.textOffset; int32 end; if (lineIndex < lineCount - 1) end = fLineInfos.ItemAtFast(lineIndex + 1).textOffset - 1; else end = fGlyphInfos.CountItems() - 1; // TODO: Optimize, can binary search offset here: for (; textOffset <= end; textOffset++) { const GlyphInfo& glyph = fGlyphInfos.ItemAtFast(textOffset); float x1 = glyph.x; if (x1 > x) return textOffset; // x2 is the location at the right bounding box of the glyph float x2 = x1 + glyph.width; // x3 is the location of the next glyph, which may be different from // x2 in case the line is justified. float x3; if (textOffset < end - 1) x3 = fGlyphInfos.ItemAtFast(textOffset + 1).x; else x3 = x2; if (x3 > x) { rightOfCenter = x > (x1 + x2) / 2.0f; return textOffset; } } // Account for trailing line break at end of line, the // returned offset should be before that. rightOfCenter = fGlyphInfos.ItemAtFast(end).charCode != '\n'; return end; }
int32 ParagraphLayout::CountLines() { _ValidateLayout(); return fLineInfos.CountItems(); }