diff --git a/modules/juce_graphics/detail/juce_SimpleShapedText.h b/modules/juce_graphics/detail/juce_SimpleShapedText.h index 40f418173b..8c9f057aee 100644 --- a/modules/juce_graphics/detail/juce_SimpleShapedText.h +++ b/modules/juce_graphics/detail/juce_SimpleShapedText.h @@ -301,6 +301,12 @@ public: */ bool isLtr (int64 glyphIndex) const; + /* This function may fail to return an out range, even if the provided textRange falls inside + the string range used for the creation of the ShapedText object. + + This is because the shaping process could fail due to insufficient glyph resolution to the + point, where it will produce zero glyphs for the provided string. + */ void getGlyphRanges (Range textRange, std::vector>& outRanges) const; /* Returns the input codepoint index that follows the glyph in a logical sense. So for LTR text diff --git a/modules/juce_gui_basics/widgets/juce_TextEditor.cpp b/modules/juce_gui_basics/widgets/juce_TextEditor.cpp index ba044e31ae..f7bad6dfac 100644 --- a/modules/juce_gui_basics/widgets/juce_TextEditor.cpp +++ b/modules/juce_gui_basics/widgets/juce_TextEditor.cpp @@ -889,14 +889,20 @@ TextEditor::CaretEdge TextEditor::getTextSelectionEdge (int index, Edge edge) co auto& paragraph = paragraphIt->value; const auto& shapedText = paragraph->getShapedText(); - const auto glyphRange = std::invoke ([&] + const auto glyphRange = std::invoke ([&]() -> Range { std::vector> g; shapedText.getGlyphRanges (textRange - paragraph->getRange().getStart(), g); - jassert (! g.empty()); + + if (g.empty()) + return {}; + return g.front(); }); + if (glyphRange.isEmpty()) + return getDefaultCursorEdge(); + const auto glyphsBounds = shapedText.getGlyphsBounds (glyphRange).getRectangle (0); const auto ltr = shapedText.isLtr (glyphRange.getStart()); @@ -2139,6 +2145,21 @@ bool TextEditor::isEmpty() const return getTotalNumChars() == 0; } +float TextEditor::getJustificationOffsetX() const +{ + const auto bottomRightX = (float) getMaximumTextWidth(); + + if (justification.testFlags (Justification::horizontallyCentred)) return jmax (0.0f, bottomRightX * 0.5f); + if (justification.testFlags (Justification::right)) return jmax (0.0f, bottomRightX); + + return 0.0f; +} + +TextEditor::CaretEdge TextEditor::getDefaultCursorEdge() const +{ + return { { getJustificationOffsetX(), 0.0f }, currentFont.getHeight() }; +} + TextEditor::CaretEdge TextEditor::getCursorEdge (const CaretState& tempCaret) const { const auto visualIndex = tempCaret.getVisualIndex(); @@ -2147,18 +2168,8 @@ TextEditor::CaretEdge TextEditor::getCursorEdge (const CaretState& tempCaret) co if (getWordWrapWidth() <= 0) return { {}, currentFont.getHeight() }; - const auto getJustificationOffsetX = [&] - { - const auto bottomRightX = (float) getMaximumTextWidth(); - - if (justification.testFlags (Justification::horizontallyCentred)) return jmax (0.0f, bottomRightX * 0.5f); - if (justification.testFlags (Justification::right)) return jmax (0.0f, bottomRightX); - - return 0.0f; - }; - if (textStorage->isEmpty()) - return { { getJustificationOffsetX(), 0.0f }, currentFont.getHeight() }; + return getDefaultCursorEdge(); if (visualIndex == getTotalNumChars()) { diff --git a/modules/juce_gui_basics/widgets/juce_TextEditor.h b/modules/juce_gui_basics/widgets/juce_TextEditor.h index 7c8504e6c7..4840f9852e 100644 --- a/modules/juce_gui_basics/widgets/juce_TextEditor.h +++ b/modules/juce_gui_basics/widgets/juce_TextEditor.h @@ -913,6 +913,8 @@ private: float height{}; }; + float getJustificationOffsetX() const; + CaretEdge getDefaultCursorEdge() const; CaretEdge getTextSelectionEdge (int index, Edge edge) const; CaretEdge getCursorEdge (const CaretState& caret) const; void updateCaretPosition();