Text fixes Bunch of text fixes: - gradients respect origin on text - changing font size updates the text editor selection - apply @avivian's transform constraint fix from https://github.com/rive-app/rive/pull/5689 to cpp Diffs= 6af562d4c Text fixes (#5696) Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head index 368df58..dfa38f4 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -fcccdeccddd2b9c47992f131f9ba741bccee5da8 +6af562d4cc2b8e309169c754a64cbc0211b9d68b
diff --git a/include/rive/text/text.hpp b/include/rive/text/text.hpp index 6cc54c0..db1818e 100644 --- a/include/rive/text/text.hpp +++ b/include/rive/text/text.hpp
@@ -221,7 +221,6 @@ std::vector<OrderedLine> m_orderedLines; GlyphRun m_ellipsisRun; std::unique_ptr<RenderPath> m_clipRenderPath; - Mat2D m_originWorldTransform; AABB m_bounds; std::vector<TextModifierGroup*> m_modifierGroups;
diff --git a/src/constraints/transform_constraint.cpp b/src/constraints/transform_constraint.cpp index 2c157e8..fa30ac4 100644 --- a/src/constraints/transform_constraint.cpp +++ b/src/constraints/transform_constraint.cpp
@@ -8,14 +8,10 @@ const Mat2D TransformConstraint::targetTransform() const { - if (originX() != 0.0f || originY() != 0.0f) - { - AABB bounds = m_Target->localBounds(); - Mat2D local = Mat2D::fromTranslate(bounds.left() + bounds.width() * originX(), - bounds.top() + bounds.height() * originY()); - return m_Target->worldTransform() * local; - } - return m_Target->worldTransform(); + AABB bounds = m_Target->localBounds(); + Mat2D local = Mat2D::fromTranslate(bounds.left() + bounds.width() * originX(), + bounds.top() + bounds.height() * originY()); + return m_Target->worldTransform() * local; } void TransformConstraint::constrain(TransformComponent* component)
diff --git a/src/text/text.cpp b/src/text/text.cpp index c99fab1..af0d763 100644 --- a/src/text/text.cpp +++ b/src/text/text.cpp
@@ -259,48 +259,71 @@ // Build up ordered runs as we go. int paragraphIndex = 0; - int lineIndex = 0; float y = 0.0f; float minY = 0.0f; - float maxX = 0.0f; + float maxWidth = 0.0f; int ellipsisLine = -1; bool isEllipsisLineLast = false; // Find the line to put the ellipsis on (line before the one that // overflows). - if (overflow() == TextOverflow::ellipsis && sizing() == TextSizing::fixed) - { - int lastLineIndex = -1; - for (const SimpleArray<GlyphLine>& paragraphLines : m_lines) - { - for (const GlyphLine& line : paragraphLines) - { - lastLineIndex++; - if (y + line.bottom <= height()) - { - ellipsisLine++; - } - } + bool wantEllipsis = overflow() == TextOverflow::ellipsis && sizing() == TextSizing::fixed; - if (!paragraphLines.empty()) - { - y += paragraphLines.back().bottom; - } - y += paragraphSpace; - } - if (ellipsisLine == -1) + int lastLineIndex = -1; + for (const SimpleArray<GlyphLine>& paragraphLines : m_lines) + { + const Paragraph& paragraph = m_shape[paragraphIndex++]; + for (const GlyphLine& line : paragraphLines) { - // Nothing fits, just show the first line and ellipse it. - ellipsisLine = 0; + const GlyphRun& endRun = paragraph.runs[line.endRunIndex]; + const GlyphRun& startRun = paragraph.runs[line.startRunIndex]; + float width = endRun.xpos[line.endGlyphIndex] - startRun.xpos[line.startGlyphIndex]; + if (width > maxWidth) + { + maxWidth = width; + } + lastLineIndex++; + if (wantEllipsis && y + line.bottom <= height()) + { + ellipsisLine++; + } } - isEllipsisLineLast = lastLineIndex == ellipsisLine; - y = 0.0f; + + if (!paragraphLines.empty()) + { + y += paragraphLines.back().bottom; + } + y += paragraphSpace; } + if (wantEllipsis && ellipsisLine == -1) + { + // Nothing fits, just show the first line and ellipse it. + ellipsisLine = 0; + } + isEllipsisLineLast = lastLineIndex == ellipsisLine; + + int lineIndex = 0; + paragraphIndex = 0; if (textOrigin() == TextOrigin::baseline && !m_lines.empty() && !m_lines[0].empty()) { y -= m_lines[0][0].baseline; minY = y; } + switch (sizing()) + { + case TextSizing::autoWidth: + m_bounds = AABB(0.0f, minY, maxWidth, std::max(minY, y - paragraphSpace)); + break; + case TextSizing::autoHeight: + m_bounds = AABB(0.0f, minY, width(), std::max(minY, y - paragraphSpace)); + break; + case TextSizing::fixed: + m_bounds = AABB(0.0f, minY, width(), minY + height()); + break; + } + + y = -m_bounds.height() * originY(); + paragraphIndex = 0; bool hasModifiers = haveModifiers(); if (hasModifiers) @@ -345,7 +368,7 @@ &m_ellipsisRun)); } const OrderedLine& orderedLine = m_orderedLines[lineIndex]; - float x = line.startX; + float x = -m_bounds.width() * originX() + line.startX; float renderY = y + line.baseline; for (auto glyphItr : orderedLine) { @@ -415,10 +438,6 @@ style->propagateOpacity(renderOpacity()); } } - if (x > maxX) - { - maxX = x; - } if (lineIndex == ellipsisLine) { return; @@ -431,25 +450,6 @@ } y += paragraphSpace; } - switch (sizing()) - { - case TextSizing::autoWidth: - m_bounds = AABB(0.0f, minY, maxX, std::max(minY, y - paragraphSpace)); - break; - case TextSizing::autoHeight: - m_bounds = AABB(0.0f, minY, width(), std::max(minY, y - paragraphSpace)); - break; - case TextSizing::fixed: - m_bounds = AABB(0.0f, minY, width(), minY + height()); - break; - } -} - -void Text::updateOriginWorldTransform() -{ - m_originWorldTransform = - m_WorldTransform * - Mat2D::fromTranslate(-m_bounds.width() * originX(), -m_bounds.height() * originY()); } const TextStyle* Text::styleFromShaperId(uint16_t id) const @@ -466,7 +466,7 @@ // transformations. renderer->save(); } - renderer->transform(m_originWorldTransform); + renderer->transform(m_WorldTransform); if (overflow() == TextOverflow::clipped && m_clipRenderPath) { renderer->clipPath(m_clipRenderPath.get()); @@ -624,11 +624,6 @@ { Super::update(value); - if (hasDirt(value, ComponentDirt::WorldTransform)) - { - updateOriginWorldTransform(); - } - if (hasDirt(value, ComponentDirt::Path)) { // We have modifiers that need shaping we'll need to compute the coverage