mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Add ShapedText::getHeight()
This commit is contained in:
parent
213d3fb56a
commit
a07098d479
5 changed files with 78 additions and 28 deletions
|
|
@ -270,18 +270,26 @@ JustifiedText::JustifiedText (const SimpleShapedText* t, const ShapedTextOptions
|
|||
: lineLength.withoutTrailingWhitespaces);
|
||||
}
|
||||
|
||||
auto y = options.isBaselineAtZero() ? 0.0f
|
||||
: getCrossAxisStartingAnchor (options.getJustification(),
|
||||
lineInfos,
|
||||
options.getHeight(),
|
||||
leading);
|
||||
auto baseline = options.isBaselineAtZero() ? 0.0f
|
||||
: getCrossAxisStartingAnchor (options.getJustification(),
|
||||
lineInfos,
|
||||
options.getHeight(),
|
||||
leading);
|
||||
|
||||
for (const auto [lineIndex, lineInfo] : enumerate (lineInfos))
|
||||
{
|
||||
const auto range = shapedText.getLineNumbers().getItem ((size_t) lineIndex).range;
|
||||
const auto lineNumber = shapedText.getLineNumbers().getItem ((size_t) lineIndex);
|
||||
const auto range = lineNumber.range;
|
||||
|
||||
lineAnchors.set<detail::MergeEqualItems::no> (range,
|
||||
{ lineInfo.mainAxisLineAlignment.anchor, y });
|
||||
const auto maxDescent = lineInfo.lineHeight - lineInfo.maxAscent;
|
||||
const auto nextLineTop = baseline + (1.0f + leading) * maxDescent + options.getAdditiveLineSpacing();
|
||||
|
||||
linesMetrics.set<detail::MergeEqualItems::no> (range,
|
||||
{ lineNumber.value,
|
||||
{ lineInfo.mainAxisLineAlignment.anchor, baseline },
|
||||
lineInfo.maxAscent,
|
||||
lineInfo.lineHeight - lineInfo.maxAscent,
|
||||
nextLineTop });
|
||||
|
||||
whitespaceStretch.set (range, 0.0f);
|
||||
const auto stretchRange = lineInfo.mainAxisLineAlignment.stretchableWhitespaces + range.getStart();
|
||||
|
|
@ -289,10 +297,8 @@ JustifiedText::JustifiedText (const SimpleShapedText* t, const ShapedTextOptions
|
|||
whitespaceStretch.set (stretchRange,
|
||||
lineInfo.mainAxisLineAlignment.extraWhitespaceAdvance);
|
||||
|
||||
const auto maxDescent = lineInfo.lineHeight - lineInfo.maxAscent;
|
||||
const auto nextLineMaxAscent = lineIndex < (int) lineInfos.size() - 1 ? lineInfos[(size_t) lineIndex + 1].maxAscent : 0.0f;
|
||||
|
||||
y += (1.0f + leading) * (maxDescent + nextLineMaxAscent) + options.getAdditiveLineSpacing();
|
||||
baseline = nextLineTop + (1.0f + leading) * nextLineMaxAscent;
|
||||
}
|
||||
|
||||
rangesToDraw.set ({ 0, (int64) shapedText.getGlyphs().size() }, DrawType::normal);
|
||||
|
|
@ -303,11 +309,11 @@ JustifiedText::JustifiedText (const SimpleShapedText* t, const ShapedTextOptions
|
|||
// The remaining logic below is for supporting
|
||||
// GlyphArrangement::addFittedText() when the maximum number of lines is
|
||||
// constrained.
|
||||
if (lineAnchors.isEmpty())
|
||||
if (linesMetrics.isEmpty())
|
||||
return;
|
||||
|
||||
const auto lastLineAlignment = lineAnchors.back();
|
||||
const auto lastLineGlyphRange = lastLineAlignment.range;
|
||||
const auto lastLineMetrics = linesMetrics.back();
|
||||
const auto lastLineGlyphRange = lastLineMetrics.range;
|
||||
const auto lastLineGlyphs = shapedText.getGlyphs (lastLineGlyphRange);
|
||||
const auto lastLineLengths = getMainAxisLineLength (lastLineGlyphs);
|
||||
|
||||
|
|
@ -318,7 +324,7 @@ JustifiedText::JustifiedText (const SimpleShapedText* t, const ShapedTextOptions
|
|||
|| effectiveLength <= *options.getMaxWidth() + maxWidthTolerance)
|
||||
return;
|
||||
|
||||
const auto cutoffAtFront = lastLineAlignment.value.getX() < 0.0f - maxWidthTolerance;
|
||||
const auto cutoffAtFront = lastLineMetrics.value.anchor.getX() < 0.0f - maxWidthTolerance;
|
||||
|
||||
const auto getLastLineVisibleRange = [&] (float ellipsisLength)
|
||||
{
|
||||
|
|
@ -437,12 +443,20 @@ JustifiedText::JustifiedText (const SimpleShapedText* t, const ShapedTextOptions
|
|||
options.getTrailingWhitespacesShouldFit());
|
||||
}();
|
||||
|
||||
lastLineAlignment.value.setX (realign.anchor);
|
||||
lastLineMetrics.value.anchor.setX (realign.anchor);
|
||||
whitespaceStretch.set (lastLineGlyphRange, 0.0f);
|
||||
whitespaceStretch.set (realign.stretchableWhitespaces + lastLineVisibleRange.getStart(),
|
||||
realign.extraWhitespaceAdvance);
|
||||
}
|
||||
|
||||
float JustifiedText::getHeight() const
|
||||
{
|
||||
if (linesMetrics.isEmpty())
|
||||
return 0.0f;
|
||||
|
||||
return linesMetrics.back().value.nextLineTop;
|
||||
}
|
||||
|
||||
void drawJustifiedText (const JustifiedText& text, const Graphics& g, AffineTransform transform)
|
||||
{
|
||||
auto& context = g.getInternalContext();
|
||||
|
|
|
|||
|
|
@ -47,6 +47,21 @@ constexpr auto partiallyUnpack (Tuple&& tuple)
|
|||
return partiallyUnpackImpl<StartingAt> (std::forward<Tuple> (tuple), std::make_index_sequence<NumElems>{});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct LineMetrics
|
||||
{
|
||||
int64 lineNumber;
|
||||
Point<float> anchor;
|
||||
float maxAscent;
|
||||
float maxDescent;
|
||||
|
||||
/* This will be below the current line's visual bottom if non-default leading or additive
|
||||
line spacing is used.
|
||||
*/
|
||||
float nextLineTop;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class JustifiedText
|
||||
{
|
||||
private:
|
||||
|
|
@ -83,18 +98,17 @@ public:
|
|||
std::optional<int64> lastLine;
|
||||
Point<float> anchor {};
|
||||
|
||||
for (const auto item : makeIntersectingRangedValues (&shapedText.getLineNumbers(),
|
||||
&shapedText.getResolvedFonts(),
|
||||
&lineAnchors,
|
||||
for (const auto item : makeIntersectingRangedValues (&shapedText.getResolvedFonts(),
|
||||
&linesMetrics,
|
||||
&rangesToDraw,
|
||||
&whitespaceStretch,
|
||||
(&rangedValues)...))
|
||||
{
|
||||
const auto& [range, line, font, lineAnchor, drawType, stretch] = partiallyUnpack<0, 6> (item);
|
||||
const auto& rest = partiallyUnpack<6, std::tuple_size_v<decltype (item)> - 6> (item);
|
||||
const auto& [range, font, lineMetrics, drawType, stretch] = partiallyUnpack<0, 5> (item);
|
||||
const auto& rest = partiallyUnpack<5, std::tuple_size_v<decltype (item)> - 5> (item);
|
||||
|
||||
if (std::exchange (lastLine, line) != line)
|
||||
anchor = lineAnchor;
|
||||
if (std::exchange (lastLine, lineMetrics.lineNumber) != lineMetrics.lineNumber)
|
||||
anchor = lineMetrics.anchor;
|
||||
|
||||
const auto glyphs = [this, r = range, dt = drawType]() -> Span<const ShapedGlyph>
|
||||
{
|
||||
|
|
@ -124,7 +138,7 @@ public:
|
|||
const auto callbackFont =
|
||||
drawType == DrawType::ellipsis ? ellipsis->getResolvedFonts().front().value : font;
|
||||
const auto callbackParameters =
|
||||
std::tuple_cat (std::tie (glyphs, positions, callbackFont, range, line), rest);
|
||||
std::tuple_cat (std::tie (glyphs, positions, callbackFont, range, lineMetrics), rest);
|
||||
|
||||
const auto invokeNullChecked = [&] (auto&... params)
|
||||
{ NullCheckedInvocation::invoke (callback, params...); };
|
||||
|
|
@ -139,9 +153,19 @@ public:
|
|||
*/
|
||||
auto& getMinimumRequiredWidthForLines() const { return minimumRequiredWidthsForLine; }
|
||||
|
||||
/* Returns the vertical distance from the baseline of the first line to the bottom of the last
|
||||
plus any additional line spacing that follows from the leading and additiveLineSpacing
|
||||
members of the ShapedTextOptions object.
|
||||
|
||||
This guarantees that if ShapedText object1 is drawn at y = 0 and object2 is drawn at
|
||||
y = object1.getHeight(), then the two texts will be spaced exactly as if they were a single
|
||||
ShapedText object containing both texts.
|
||||
*/
|
||||
float getHeight() const;
|
||||
|
||||
private:
|
||||
const SimpleShapedText& shapedText;
|
||||
detail::RangedValues<Point<float>> lineAnchors;
|
||||
detail::RangedValues<LineMetrics> linesMetrics;
|
||||
std::optional<SimpleShapedText> ellipsis;
|
||||
detail::RangedValues<DrawType> rangesToDraw;
|
||||
detail::RangedValues<float> whitespaceStretch;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ public:
|
|||
drawJustifiedText (justifiedText, g, transform);
|
||||
}
|
||||
|
||||
float getHeight() const
|
||||
{
|
||||
return justifiedText.getHeight();
|
||||
}
|
||||
|
||||
auto& getText() const
|
||||
{
|
||||
return text;
|
||||
|
|
@ -94,6 +99,11 @@ void ShapedText::draw (const Graphics& g, AffineTransform transform) const
|
|||
impl->draw (g, transform);
|
||||
}
|
||||
|
||||
float ShapedText::getHeight() const
|
||||
{
|
||||
return impl->getHeight();
|
||||
}
|
||||
|
||||
const String& ShapedText::getText() const
|
||||
{
|
||||
return impl->getText();
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ public:
|
|||
/* Draws the text. */
|
||||
void draw (const Graphics& g, AffineTransform transform) const;
|
||||
|
||||
float getHeight() const;
|
||||
|
||||
/* @internal */
|
||||
const JustifiedText& getJustifiedText() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -427,10 +427,10 @@ void TextLayout::createStandardLayout (const AttributedString& text)
|
|||
Span<Point<float>> positions,
|
||||
Font font,
|
||||
Range<int64> glyphRange,
|
||||
int64 lineNumber,
|
||||
LineMetrics lineMetrics,
|
||||
Colour colour)
|
||||
{
|
||||
if (std::exchange (lastLineNumber, lineNumber) != lineNumber)
|
||||
if (std::exchange (lastLineNumber, lineMetrics.lineNumber) != lineMetrics.lineNumber)
|
||||
{
|
||||
if (line != nullptr)
|
||||
addLine (std::move (line));
|
||||
|
|
@ -438,7 +438,7 @@ void TextLayout::createStandardLayout (const AttributedString& text)
|
|||
const auto ascentAndDescent = getMaxFontAscentAndDescentInEnclosingLine (st,
|
||||
glyphRange);
|
||||
|
||||
line = std::make_unique<Line> (castTo<int> (getLineInputRange (st, lineNumber)),
|
||||
line = std::make_unique<Line> (castTo<int> (getLineInputRange (st, lineMetrics.lineNumber)),
|
||||
positions[0],
|
||||
ascentAndDescent.ascent,
|
||||
ascentAndDescent.descent,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue