mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Modified TextLayout creation functions to take an optional maximum height as well as a maximum width.
This commit is contained in:
parent
04cb9bf3e8
commit
a49baa3e52
3 changed files with 121 additions and 75 deletions
|
|
@ -22,8 +22,8 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
TextLayout::Glyph::Glyph (const int glyphCode_, Point<float> anchor_, float width_) noexcept
|
||||
: glyphCode (glyphCode_), anchor (anchor_), width (width_)
|
||||
TextLayout::Glyph::Glyph (const int glyph, Point<float> anch, float w) noexcept
|
||||
: glyphCode (glyph), anchor (anch), width (w)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -70,11 +70,10 @@ TextLayout::Line::Line() noexcept
|
|||
{
|
||||
}
|
||||
|
||||
TextLayout::Line::Line (Range<int> stringRange_, Point<float> lineOrigin_,
|
||||
const float ascent_, const float descent_, const float leading_,
|
||||
const int numRunsToPreallocate)
|
||||
: stringRange (stringRange_), lineOrigin (lineOrigin_),
|
||||
ascent (ascent_), descent (descent_), leading (leading_)
|
||||
TextLayout::Line::Line (Range<int> range, Point<float> o, float asc, float desc,
|
||||
float lead, int numRunsToPreallocate)
|
||||
: stringRange (range), lineOrigin (o),
|
||||
ascent (asc), descent (desc), leading (lead)
|
||||
{
|
||||
runs.ensureStorageAllocated (numRunsToPreallocate);
|
||||
}
|
||||
|
|
@ -127,14 +126,28 @@ Range<float> TextLayout::Line::getLineBoundsX() const noexcept
|
|||
return range + lineOrigin.x;
|
||||
}
|
||||
|
||||
Range<float> TextLayout::Line::getLineBoundsY() const noexcept
|
||||
{
|
||||
return Range<float> (lineOrigin.y - ascent,
|
||||
lineOrigin.y + descent);
|
||||
}
|
||||
|
||||
Rectangle<float> TextLayout::Line::getLineBounds() const noexcept
|
||||
{
|
||||
const Range<float> x (getLineBoundsX()),
|
||||
y (getLineBoundsY());
|
||||
|
||||
return Rectangle<float> (x.getStart(), y.getStart(), x.getLength(), y.getLength());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
TextLayout::TextLayout()
|
||||
: width (0), justification (Justification::topLeft)
|
||||
: width (0), height (0), justification (Justification::topLeft)
|
||||
{
|
||||
}
|
||||
|
||||
TextLayout::TextLayout (const TextLayout& other)
|
||||
: width (other.width),
|
||||
: width (other.width), height (other.height),
|
||||
justification (other.justification)
|
||||
{
|
||||
lines.addCopiesOf (other.lines);
|
||||
|
|
@ -142,16 +155,17 @@ TextLayout::TextLayout (const TextLayout& other)
|
|||
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
TextLayout::TextLayout (TextLayout&& other) noexcept
|
||||
: lines (static_cast <OwnedArray<Line>&&> (other.lines)),
|
||||
width (other.width),
|
||||
: lines (static_cast<OwnedArray<Line>&&> (other.lines)),
|
||||
width (other.width), height (other.height),
|
||||
justification (other.justification)
|
||||
{
|
||||
}
|
||||
|
||||
TextLayout& TextLayout::operator= (TextLayout&& other) noexcept
|
||||
{
|
||||
lines = static_cast <OwnedArray<Line>&&> (other.lines);
|
||||
lines = static_cast<OwnedArray<Line>&&> (other.lines);
|
||||
width = other.width;
|
||||
height = other.height;
|
||||
justification = other.justification;
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -160,6 +174,7 @@ TextLayout& TextLayout::operator= (TextLayout&& other) noexcept
|
|||
TextLayout& TextLayout::operator= (const TextLayout& other)
|
||||
{
|
||||
width = other.width;
|
||||
height = other.height;
|
||||
justification = other.justification;
|
||||
lines.clear();
|
||||
lines.addCopiesOf (other.lines);
|
||||
|
|
@ -170,17 +185,9 @@ TextLayout::~TextLayout()
|
|||
{
|
||||
}
|
||||
|
||||
float TextLayout::getHeight() const noexcept
|
||||
{
|
||||
if (const Line* const lastLine = lines.getLast())
|
||||
return lastLine->lineOrigin.y + lastLine->descent;
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
TextLayout::Line& TextLayout::getLine (const int index) const
|
||||
{
|
||||
return *lines[index];
|
||||
return *lines.getUnchecked (index);
|
||||
}
|
||||
|
||||
void TextLayout::ensureStorageAllocated (int numLinesNeeded)
|
||||
|
|
@ -199,7 +206,7 @@ void TextLayout::draw (Graphics& g, const Rectangle<float>& area) const
|
|||
|
||||
LowLevelGraphicsContext& context = g.getInternalContext();
|
||||
|
||||
for (int i = 0; i < getNumLines(); ++i)
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
const Line& line = getLine (i);
|
||||
const Point<float> lineOrigin (origin + line.lineOrigin);
|
||||
|
|
@ -221,15 +228,60 @@ void TextLayout::draw (Graphics& g, const Rectangle<float>& area) const
|
|||
}
|
||||
|
||||
void TextLayout::createLayout (const AttributedString& text, float maxWidth)
|
||||
{
|
||||
createLayout (text, maxWidth, 1.0e7f);
|
||||
}
|
||||
|
||||
void TextLayout::createLayout (const AttributedString& text, float maxWidth, float maxHeight)
|
||||
{
|
||||
lines.clear();
|
||||
width = maxWidth;
|
||||
height = maxHeight;
|
||||
justification = text.getJustification();
|
||||
|
||||
if (! createNativeLayout (text))
|
||||
createStandardLayout (text);
|
||||
|
||||
recalculateWidth (text);
|
||||
recalculateSize (text);
|
||||
}
|
||||
|
||||
void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth)
|
||||
{
|
||||
createLayoutWithBalancedLineLengths (text, maxWidth, 1.0e7f);
|
||||
}
|
||||
|
||||
void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth, float maxHeight)
|
||||
{
|
||||
const float minimumWidth = maxWidth / 2.0f;
|
||||
float bestWidth = maxWidth;
|
||||
float bestLineProportion = 0.0f;
|
||||
|
||||
while (maxWidth > minimumWidth)
|
||||
{
|
||||
createLayout (text, maxWidth, maxHeight);
|
||||
|
||||
if (getNumLines() < 2)
|
||||
return;
|
||||
|
||||
const float line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength();
|
||||
const float line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength();
|
||||
const float shortestLine = jmin (line1, line2);
|
||||
const float prop = (shortestLine > 0) ? jmax (line1, line2) / shortestLine : 1.0f;
|
||||
|
||||
if (prop > 0.9f)
|
||||
return;
|
||||
|
||||
if (prop > bestLineProportion)
|
||||
{
|
||||
bestLineProportion = prop;
|
||||
bestWidth = maxWidth;
|
||||
}
|
||||
|
||||
maxWidth -= 10.0f;
|
||||
}
|
||||
|
||||
if (bestWidth != maxWidth)
|
||||
createLayout (text, bestWidth, maxHeight);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -305,8 +357,8 @@ namespace TextLayoutHelpers
|
|||
{
|
||||
const Token& t = *tokens.getUnchecked (i);
|
||||
|
||||
Array <int> newGlyphs;
|
||||
Array <float> xOffsets;
|
||||
Array<int> newGlyphs;
|
||||
Array<float> xOffsets;
|
||||
t.font.getGlyphPositions (getTrimmedEndIfNotAllWhitespace (t.text), newGlyphs, xOffsets);
|
||||
|
||||
if (currentRun == nullptr) currentRun = new TextLayout::Run();
|
||||
|
|
@ -561,41 +613,6 @@ namespace TextLayoutHelpers
|
|||
};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth)
|
||||
{
|
||||
const float minimumWidth = maxWidth / 2.0f;
|
||||
float bestWidth = maxWidth;
|
||||
float bestLineProportion = 0.0f;
|
||||
|
||||
while (maxWidth > minimumWidth)
|
||||
{
|
||||
createLayout (text, maxWidth);
|
||||
|
||||
if (getNumLines() < 2)
|
||||
return;
|
||||
|
||||
const float line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength();
|
||||
const float line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength();
|
||||
const float shortestLine = jmin (line1, line2);
|
||||
const float prop = (shortestLine > 0) ? jmax (line1, line2) / shortestLine : 1.0f;
|
||||
|
||||
if (prop > 0.9f)
|
||||
return;
|
||||
|
||||
if (prop > bestLineProportion)
|
||||
{
|
||||
bestLineProportion = prop;
|
||||
bestWidth = maxWidth;
|
||||
}
|
||||
|
||||
maxWidth -= 10.0f;
|
||||
}
|
||||
|
||||
if (bestWidth != maxWidth)
|
||||
createLayout (text, bestWidth);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TextLayout::createStandardLayout (const AttributedString& text)
|
||||
{
|
||||
|
|
@ -603,18 +620,19 @@ void TextLayout::createStandardLayout (const AttributedString& text)
|
|||
l.createLayout (text, *this);
|
||||
}
|
||||
|
||||
void TextLayout::recalculateWidth (const AttributedString& text)
|
||||
void TextLayout::recalculateSize (const AttributedString& text)
|
||||
{
|
||||
if (lines.size() > 0 && text.getReadingDirection() != AttributedString::rightToLeft)
|
||||
{
|
||||
Range<float> range (lines.getFirst()->getLineBoundsX());
|
||||
Rectangle<float> bounds (lines.getFirst()->getLineBounds());
|
||||
|
||||
for (int i = lines.size(); --i > 0;)
|
||||
range = range.getUnionWith (lines.getUnchecked(i)->getLineBoundsX());
|
||||
bounds = bounds.getUnion (lines.getUnchecked(i)->getLineBounds());
|
||||
|
||||
for (int i = lines.size(); --i >= 0;)
|
||||
lines.getUnchecked(i)->lineOrigin.x -= range.getStart();
|
||||
lines.getUnchecked(i)->lineOrigin.x -= bounds.getX();
|
||||
|
||||
width = range.getLength();
|
||||
width = bounds.getWidth();
|
||||
height = bounds.getHeight();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public:
|
|||
TextLayout (const TextLayout&);
|
||||
TextLayout& operator= (const TextLayout&);
|
||||
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
|
||||
TextLayout (TextLayout&& other) noexcept;
|
||||
TextLayout (TextLayout&&) noexcept;
|
||||
TextLayout& operator= (TextLayout&&) noexcept;
|
||||
#endif
|
||||
|
||||
|
|
@ -57,7 +57,12 @@ public:
|
|||
/** Creates a layout from the given attributed string.
|
||||
This will replace any data that is currently stored in the layout.
|
||||
*/
|
||||
void createLayout (const AttributedString& text, float maxWidth);
|
||||
void createLayout (const AttributedString&, float maxWidth);
|
||||
|
||||
/** Creates a layout from the given attributed string, given some size constraints.
|
||||
This will replace any data that is currently stored in the layout.
|
||||
*/
|
||||
void createLayout (const AttributedString&, float maxWidth, float maxHeight);
|
||||
|
||||
/** Creates a layout, attempting to choose a width which results in lines
|
||||
of a similar length.
|
||||
|
|
@ -65,13 +70,21 @@ public:
|
|||
This will be slower than the normal createLayout method, but produces a
|
||||
tidier result.
|
||||
*/
|
||||
void createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth);
|
||||
void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth);
|
||||
|
||||
/** Creates a layout, attempting to choose a width which results in lines
|
||||
of a similar length.
|
||||
|
||||
This will be slower than the normal createLayout method, but produces a
|
||||
tidier result.
|
||||
*/
|
||||
void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth, float maxHeight);
|
||||
|
||||
/** Draws the layout within the specified area.
|
||||
The position of the text within the rectangle is controlled by the justification
|
||||
flags set in the original AttributedString that was used to create this layout.
|
||||
*/
|
||||
void draw (Graphics& g, const Rectangle<float>& area) const;
|
||||
void draw (Graphics&, const Rectangle<float>& area) const;
|
||||
|
||||
//==============================================================================
|
||||
/** A positioned glyph. */
|
||||
|
|
@ -131,6 +144,12 @@ public:
|
|||
/** Returns the X position range which contains all the glyphs in this line. */
|
||||
Range<float> getLineBoundsX() const noexcept;
|
||||
|
||||
/** Returns the Y position range which contains all the glyphs in this line. */
|
||||
Range<float> getLineBoundsY() const noexcept;
|
||||
|
||||
/** Returns the smallest rectangle which contains all the glyphs in this line. */
|
||||
Rectangle<float> getLineBounds() const noexcept;
|
||||
|
||||
OwnedArray<Run> runs; /**< The glyph-runs in this line. */
|
||||
Range<int> stringRange; /**< The character range that this line represents in the
|
||||
original string that was used to create it. */
|
||||
|
|
@ -147,7 +166,7 @@ public:
|
|||
float getWidth() const noexcept { return width; }
|
||||
|
||||
/** Returns the maximum height of the content. */
|
||||
float getHeight() const noexcept;
|
||||
float getHeight() const noexcept { return height; }
|
||||
|
||||
/** Returns the number of lines in the layout. */
|
||||
int getNumLines() const noexcept { return lines.size(); }
|
||||
|
|
@ -157,19 +176,19 @@ 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* line);
|
||||
void addLine (Line*);
|
||||
|
||||
/** Pre-allocates space for the specified number of lines. */
|
||||
void ensureStorageAllocated (int numLinesNeeded);
|
||||
|
||||
private:
|
||||
OwnedArray<Line> lines;
|
||||
float width;
|
||||
float width, height;
|
||||
Justification justification;
|
||||
|
||||
void createStandardLayout (const AttributedString&);
|
||||
bool createNativeLayout (const AttributedString&);
|
||||
void recalculateWidth (const AttributedString&);
|
||||
void recalculateSize (const AttributedString&);
|
||||
|
||||
JUCE_LEAK_DETECTOR (TextLayout)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -311,6 +311,13 @@ namespace DirectWriteTypeLayout
|
|||
|
||||
setTextFormatProperties (text, dwTextFormat);
|
||||
|
||||
{
|
||||
DWRITE_TRIMMING trimming = { DWRITE_TRIMMING_GRANULARITY_CHARACTER, 0, 0 };
|
||||
ComSmartPtr<IDWriteInlineObject> trimmingSign;
|
||||
hr = directWriteFactory->CreateEllipsisTrimmingSign (dwTextFormat, trimmingSign.resetAndGetPointerAddress());
|
||||
hr = dwTextFormat->SetTrimming (&trimming, trimmingSign);
|
||||
}
|
||||
|
||||
const int textLen = text.getText().length();
|
||||
|
||||
hr = directWriteFactory->CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat,
|
||||
|
|
@ -344,7 +351,8 @@ namespace DirectWriteTypeLayout
|
|||
|
||||
ComSmartPtr<IDWriteTextLayout> dwTextLayout;
|
||||
|
||||
if (! setupLayout (text, layout.getWidth(), 1.0e7f, renderTarget, directWriteFactory, fontCollection, dwTextLayout))
|
||||
if (! setupLayout (text, layout.getWidth(), layout.getHeight(), renderTarget,
|
||||
directWriteFactory, fontCollection, dwTextLayout))
|
||||
return;
|
||||
|
||||
UINT32 actualLineCount = 0;
|
||||
|
|
@ -374,7 +382,8 @@ namespace DirectWriteTypeLayout
|
|||
{
|
||||
ComSmartPtr<IDWriteTextLayout> dwTextLayout;
|
||||
|
||||
if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget, directWriteFactory, fontCollection, dwTextLayout))
|
||||
if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget,
|
||||
directWriteFactory, fontCollection, dwTextLayout))
|
||||
{
|
||||
ComSmartPtr<ID2D1SolidColorBrush> d2dBrush;
|
||||
renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f)),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue