1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

TextLayout: Fixed some bugs setting stringRanges

This commit is contained in:
reuk 2019-08-08 17:00:42 +01:00 committed by Tom Poole
parent 76f3aec386
commit edf99d171f
4 changed files with 170 additions and 31 deletions

View file

@ -27,6 +27,11 @@
namespace juce
{
static String substring (const String& text, Range<int> range)
{
return text.substring (range.getStart(), range.getEnd());
}
TextLayout::Glyph::Glyph (int glyph, Point<float> anch, float w) noexcept
: glyphCode (glyph), anchor (anch), width (w)
{
@ -207,9 +212,9 @@ void TextLayout::ensureStorageAllocated (int numLinesNeeded)
lines.ensureStorageAllocated (numLinesNeeded);
}
void TextLayout::addLine (Line* line)
void TextLayout::addLine (std::unique_ptr<Line> line)
{
lines.add (line);
lines.add (line.release());
}
void TextLayout::draw (Graphics& g, Rectangle<float> area) const
@ -221,9 +226,9 @@ void TextLayout::draw (Graphics& g, Rectangle<float> area) const
auto clipTop = clip.getY() - origin.y;
auto clipBottom = clip.getBottom() - origin.y;
for (auto* line : lines)
for (auto& line : *this)
{
auto lineRangeY = line->getLineBoundsY();
auto lineRangeY = line.getLineBoundsY();
if (lineRangeY.getEnd() < clipTop)
continue;
@ -231,9 +236,9 @@ void TextLayout::draw (Graphics& g, Rectangle<float> area) const
if (lineRangeY.getStart() > clipBottom)
break;
auto lineOrigin = origin + line->lineOrigin;
auto lineOrigin = origin + line.lineOrigin;
for (auto* run : line->runs)
for (auto* run : line.runs)
{
context.setFont (run->font);
context.setFill (run->colour);
@ -363,8 +368,8 @@ namespace TextLayoutHelpers
Array<float> xOffsets;
t.font.getGlyphPositions (getTrimmedEndIfNotAllWhitespace (t.text), newGlyphs, xOffsets);
if (currentRun == nullptr) currentRun .reset (new TextLayout::Run());
if (currentLine == nullptr) currentLine.reset (new TextLayout::Line());
if (currentRun == nullptr) currentRun = std::make_unique<TextLayout::Run>();
if (currentLine == nullptr) currentLine = std::make_unique<TextLayout::Line>();
if (newGlyphs.size() > 0)
{
@ -389,9 +394,10 @@ namespace TextLayoutHelpers
charPosition += newGlyphs.size();
}
if (t.isWhitespace || t.isNewLine)
else if (t.isWhitespace || t.isNewLine)
{
++charPosition;
}
if (auto* nextToken = tokens[i + 1])
{
@ -404,13 +410,13 @@ namespace TextLayoutHelpers
if (t.line != nextToken->line)
{
if (currentRun == nullptr)
currentRun.reset (new TextLayout::Run());
currentRun = std::make_unique<TextLayout::Run>();
addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition);
currentLine->stringRange = { lineStartPosition, charPosition };
if (! needToSetLineOrigin)
layout.addLine (currentLine.release());
layout.addLine (std::move (currentLine));
runStartPosition = charPosition;
lineStartPosition = charPosition;
@ -423,7 +429,7 @@ namespace TextLayoutHelpers
currentLine->stringRange = { lineStartPosition, charPosition };
if (! needToSetLineOrigin)
layout.addLine (currentLine.release());
layout.addLine (std::move (currentLine));
needToSetLineOrigin = true;
}
@ -434,14 +440,14 @@ namespace TextLayoutHelpers
auto totalW = layout.getWidth();
bool isCentred = (text.getJustification().getFlags() & Justification::horizontallyCentred) != 0;
for (int i = 0; i < layout.getNumLines(); ++i)
for (auto& line : layout)
{
auto dx = totalW - layout.getLine(i).getLineBoundsX().getLength();
auto dx = totalW - line.getLineBoundsX().getLength();
if (isCentred)
dx /= 2.0f;
layout.getLine(i).lineOrigin.x += dx;
line.lineOrigin.x += dx;
}
}
}
@ -560,7 +566,7 @@ namespace TextLayoutHelpers
{
auto& attr = text.getAttribute (i);
appendText (text.getText().substring (attr.range.getStart(), attr.range.getEnd()),
appendText (substring (text.getText(), attr.range),
attr.font, attr.colour);
}
}
@ -611,4 +617,66 @@ void TextLayout::recalculateSize()
}
}
//==============================================================================
#if JUCE_UNIT_TESTS
struct TextLayoutTests : public UnitTest
{
TextLayoutTests()
: UnitTest ("Text Layout", UnitTestCategories::text)
{}
static TextLayout createLayout (StringRef text, float width)
{
Font fontToUse (12.0f);
AttributedString string (text);
string.setFont (std::move (fontToUse));
TextLayout layout;
layout.createLayout (std::move (string), width);
return layout;
}
void testLineBreaks (const String& line, float width, const StringArray& expected)
{
const auto layout = createLayout (line, width);
beginTest ("A line is split into expected pieces");
{
expectEquals (layout.getNumLines(), expected.size());
const auto limit = jmin (layout.getNumLines(), expected.size());
for (int i = 0; i != limit; ++i)
expectEquals (substring (line, layout.getLine (i).stringRange), expected[i]);
}
}
void runTest() override
{
const String shortLine ("hello world");
testLineBreaks (shortLine, 1.0e7f, { shortLine });
testLineBreaks ("this line should be split",
60.0f,
{ "this line ",
"should be ",
"split" });
testLineBreaks ("these\nlines \nhave\n weird \n spacing ",
80.0f,
{ "these\n",
"lines \n",
"have\n",
" weird \n",
" spacing " });
}
};
static TextLayoutTests textLayoutTests;
#endif
} // namespace juce

View file

@ -40,6 +40,59 @@ namespace juce
*/
class JUCE_API TextLayout final
{
private:
template <typename Iterator>
class DereferencingIterator
{
public:
using value_type = typename std::remove_reference<decltype(**std::declval<Iterator>())>::type;
using difference_type = typename std::iterator_traits<Iterator>::difference_type;
using pointer = value_type*;
using reference = value_type&;
using iterator_category = typename std::iterator_traits<Iterator>::iterator_category;
explicit DereferencingIterator (Iterator in) : iterator (std::move (in)) {}
DereferencingIterator& operator+= (difference_type distance)
{
iterator += distance;
return *this;
}
friend DereferencingIterator operator+ (DereferencingIterator i, difference_type d) { return i += d; }
friend DereferencingIterator operator+ (difference_type d, DereferencingIterator i) { return i += d; }
DereferencingIterator& operator-= (difference_type distance)
{
iterator -= distance;
return *this;
}
friend DereferencingIterator operator- (DereferencingIterator i, difference_type d) { return i -= d; }
friend difference_type operator- (DereferencingIterator a, DereferencingIterator b) { return a.iterator - b.iterator; }
reference operator[] (difference_type d) const { return *iterator[d]; }
friend bool operator< (DereferencingIterator a, DereferencingIterator b) { return a.iterator < b.iterator; }
friend bool operator<= (DereferencingIterator a, DereferencingIterator b) { return a.iterator <= b.iterator; }
friend bool operator> (DereferencingIterator a, DereferencingIterator b) { return a.iterator > b.iterator; }
friend bool operator>= (DereferencingIterator a, DereferencingIterator b) { return a.iterator >= b.iterator; }
friend bool operator== (DereferencingIterator a, DereferencingIterator b) { return a.iterator == b.iterator; }
friend bool operator!= (DereferencingIterator a, DereferencingIterator b) { return a.iterator != b.iterator; }
DereferencingIterator& operator++() { ++iterator; return *this; }
DereferencingIterator& operator--() { --iterator; return *this; }
DereferencingIterator operator++ (int) const { DereferencingIterator copy (*this); ++(*this); return copy; }
DereferencingIterator operator-- (int) const { DereferencingIterator copy (*this); --(*this); return copy; }
reference operator* () const { return **iterator; }
pointer operator->() const { return *iterator; }
private:
Iterator iterator;
};
public:
/** Creates an empty layout.
Having created a TextLayout, you can populate it using createLayout() or
@ -180,11 +233,29 @@ public:
/** Adds a line to the layout. The layout will take ownership of this line object
and will delete it when it is no longer needed. */
void addLine (Line*);
void addLine (std::unique_ptr<Line>);
/** Pre-allocates space for the specified number of lines. */
void ensureStorageAllocated (int numLinesNeeded);
using iterator = DereferencingIterator< Line* const*>;
using const_iterator = DereferencingIterator<const Line* const*>;
/** Returns an iterator over the lines of content */
iterator begin() { return iterator (lines.begin()); }
const_iterator begin() const { return const_iterator (lines.begin()); }
const_iterator cbegin() const { return const_iterator (lines.begin()); }
/** Returns an iterator over the lines of content */
iterator end() { return iterator (lines.end()); }
const_iterator end() const { return const_iterator (lines.end()); }
const_iterator cend() const { return const_iterator (lines.end()); }
/** If you modify the TextLayout after creating it, call this to compute
the new dimensions of the content.
*/
void recalculateSize();
private:
OwnedArray<Line> lines;
float width, height;
@ -192,7 +263,6 @@ private:
void createStandardLayout (const AttributedString&);
bool createNativeLayout (const AttributedString&);
void recalculateSize();
JUCE_LEAK_DETECTOR (TextLayout)
};

View file

@ -387,19 +387,18 @@ namespace CoreTextTypeLayout
auto numRuns = CFArrayGetCount (runs);
auto cfrlineStringRange = CTLineGetStringRange (line);
auto lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length - 1;
auto lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length;
Range<int> lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd);
LineInfo lineInfo (frame, line, i);
auto glyphLine = new TextLayout::Line (lineStringRange,
Point<float> ((float) lineInfo.origin.x,
(float) (boundsHeight - lineInfo.origin.y)),
(float) lineInfo.ascent,
(float) lineInfo.descent,
(float) lineInfo.leading,
(int) numRuns);
glyphLayout.addLine (glyphLine);
auto glyphLine = std::make_unique<TextLayout::Line> (lineStringRange,
Point<float> ((float) lineInfo.origin.x,
(float) (boundsHeight - lineInfo.origin.y)),
(float) lineInfo.ascent,
(float) lineInfo.descent,
(float) lineInfo.leading,
(int) numRuns);
for (CFIndex j = 0; j < numRuns; ++j)
{
@ -457,6 +456,8 @@ namespace CoreTextTypeLayout
(float) positions.points[k].y),
(float) advances.advances[k].width));
}
glyphLayout.addLine (std::move (glyphLine));
}
CFRelease (frame);

View file

@ -100,10 +100,10 @@ namespace DirectWriteTypeLayout
if (currentLine >= layout->getNumLines())
{
jassert (currentLine == layout->getNumLines());
auto line = new TextLayout::Line();
layout->addLine (line);
auto line = std::make_unique<TextLayout::Line>();
line->lineOrigin = Point<float> (baselineOriginX, baselineOriginY);
layout->addLine (std::move (line));
}
}