1
0
Fork 0
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:
attila 2025-01-29 17:54:20 +01:00 committed by Attila Szarvas
parent 213d3fb56a
commit a07098d479
5 changed files with 78 additions and 28 deletions

View file

@ -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();

View file

@ -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;

View file

@ -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();

View file

@ -79,6 +79,8 @@ public:
/* Draws the text. */
void draw (const Graphics& g, AffineTransform transform) const;
float getHeight() const;
/* @internal */
const JustifiedText& getJustifiedText() const;

View file

@ -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,