diff --git a/examples/GUI/FontsDemo.h b/examples/GUI/FontsDemo.h index a2bb5b3bcc..35785ba7dc 100644 --- a/examples/GUI/FontsDemo.h +++ b/examples/GUI/FontsDemo.h @@ -65,15 +65,22 @@ public: addAndMakeVisible (kerningLabel); addAndMakeVisible (kerningSlider); addAndMakeVisible (scaleLabel); + addAndMakeVisible (horizontalJustificationLabel); + addAndMakeVisible (verticalJustificationLabel); addAndMakeVisible (scaleSlider); addAndMakeVisible (boldToggle); addAndMakeVisible (italicToggle); addAndMakeVisible (styleBox); + addAndMakeVisible (horizontalJustificationBox); + addAndMakeVisible (verticalJustificationBox); + addAndMakeVisible (resetButton); - kerningLabel.attachToComponent (&kerningSlider, true); - heightLabel .attachToComponent (&heightSlider, true); - scaleLabel .attachToComponent (&scaleSlider, true); - styleLabel .attachToComponent (&styleBox, true); + kerningLabel .attachToComponent (&kerningSlider, true); + heightLabel .attachToComponent (&heightSlider, true); + scaleLabel .attachToComponent (&scaleSlider, true); + styleLabel .attachToComponent (&styleBox, true); + horizontalJustificationLabel.attachToComponent (&horizontalJustificationBox, true); + verticalJustificationLabel .attachToComponent (&verticalJustificationBox, true); heightSlider .addListener (this); kerningSlider.addListener (this); @@ -94,10 +101,6 @@ public: scaleSlider .setRange (0.2, 3.0, 0.01); kerningSlider.setRange (-2.0, 2.0, 0.01); - scaleSlider .setValue (1.0); // Set some initial values for the sliders. - heightSlider .setValue (20.0); - kerningSlider.setValue (0); - // set up the layout and resizer bars.. verticalLayout.setItemLayout (0, -0.2, -0.8, -0.35); // width of the font list must be // between 20% and 80%, preferably 50% @@ -127,6 +130,11 @@ public: demoTextBox.setColour (TextEditor::textColourId, Colours::black); demoTextBox.setColour (TextEditor::backgroundColourId, Colours::white); + resetButton.onClick = [this] { resetToDefaultParameters(); }; + + setupJustificationOptions(); + resetToDefaultParameters(); + setSize (750, 750); } @@ -151,7 +159,10 @@ public: r.removeFromLeft (verticalDividerBar->getRight()); - int labelWidth = 60; + resetButton.setBounds (r.removeFromBottom (30).reduced (jmax (20, r.getWidth() / 5), 0)); + r.removeFromBottom (8); + + const int labelWidth = 60; auto styleArea = r.removeFromBottom (26); styleArea.removeFromLeft (labelWidth); @@ -163,6 +174,10 @@ public: boldToggle.setBounds (row.removeFromLeft (row.getWidth() / 2)); italicToggle.setBounds (row); + r.removeFromBottom (8); + horizontalJustificationBox.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth * 3)); + r.removeFromBottom (8); + verticalJustificationBox.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth * 3)); r.removeFromBottom (8); scaleSlider.setBounds (r.removeFromBottom (30).withTrimmedLeft (labelWidth)); r.removeFromBottom (8); @@ -175,9 +190,9 @@ public: void sliderValueChanged (Slider* sliderThatWasMoved) override { - if (sliderThatWasMoved == &heightSlider) refreshPreviewBoxFont(); - else if (sliderThatWasMoved == &kerningSlider) refreshPreviewBoxFont(); - else if (sliderThatWasMoved == &scaleSlider) refreshPreviewBoxFont(); + if (sliderThatWasMoved == &heightSlider) refreshPreviewBoxFont(); + else if (sliderThatWasMoved == &kerningSlider) refreshPreviewBoxFont(); + else if (sliderThatWasMoved == &scaleSlider) refreshPreviewBoxFont(); } // The following methods implement the ListBoxModel virtual methods: @@ -215,21 +230,69 @@ private: ListBox listBox; TextEditor demoTextBox; - Label heightLabel { {}, "Height:" }, - kerningLabel { {}, "Kerning:" }, - scaleLabel { {}, "Scale:" }, - styleLabel { {}, "Style:" }; + const double defaultScale = 1.0, defaultHeight = 20.0, defaultKerning = 0.0; + const bool defaultBold = false, defaultItalic = false; + const int defaultStyle = 0, defaultHorizontalJustification = 0, defaultVerticalJustification = 0; + + Label heightLabel { {}, "Height:" }, + kerningLabel { {}, "Kerning:" }, + scaleLabel { {}, "Scale:" }, + styleLabel { {}, "Style:" }, + horizontalJustificationLabel { {}, "Justification (horizontal):" }, + verticalJustificationLabel { {}, "Justification (vertical):" }; ToggleButton boldToggle { "Bold" }, italicToggle { "Italic" }; + TextButton resetButton { "Reset" }; + Slider heightSlider, kerningSlider, scaleSlider; - ComboBox styleBox; + ComboBox styleBox, horizontalJustificationBox, verticalJustificationBox; StretchableLayoutManager verticalLayout; std::unique_ptr verticalDividerBar; + StringArray horizontalJustificationStrings { "Left", "Centred", "Right" }, + verticalJustificationStrings { "Top", "Centred", "Bottom" }; + + Array horizontalJustificationFlags { Justification::left, Justification::horizontallyCentred, Justification::right }, + verticalJustificationFlags { Justification::top, Justification::verticallyCentred, Justification::bottom}; + //============================================================================== + void resetToDefaultParameters() + { + scaleSlider .setValue (defaultScale); + heightSlider .setValue (defaultHeight); + kerningSlider.setValue (defaultKerning); + + boldToggle .setToggleState (defaultBold, sendNotificationSync); + italicToggle.setToggleState (defaultItalic, sendNotificationSync); + + styleBox.setSelectedItemIndex (defaultStyle); + horizontalJustificationBox.setSelectedItemIndex (defaultHorizontalJustification); + verticalJustificationBox .setSelectedItemIndex (defaultVerticalJustification); + } + + void setupJustificationOptions() + { + horizontalJustificationBox.addItemList (horizontalJustificationStrings, 1); + verticalJustificationBox .addItemList (verticalJustificationStrings, 1); + + auto updateJustification = [this]() + { + auto horizontalIndex = horizontalJustificationBox.getSelectedItemIndex(); + auto verticalIndex = verticalJustificationBox.getSelectedItemIndex(); + + auto horizontalJustification = horizontalJustificationFlags[horizontalIndex]; + auto verticalJustification = verticalJustificationFlags[verticalIndex]; + + demoTextBox.setJustification (horizontalJustification | verticalJustification); + }; + + horizontalJustificationBox.onChange = updateJustification; + verticalJustificationBox .onChange = updateJustification; + } + void refreshPreviewBoxFont() { auto bold = boldToggle .getToggleState(); @@ -265,7 +328,7 @@ private: styleBox.clear(); styleBox.addItemList (newStyles, 1); - styleBox.setSelectedItemIndex (0); + styleBox.setSelectedItemIndex (defaultStyle); } } diff --git a/modules/juce_gui_basics/widgets/juce_TextEditor.cpp b/modules/juce_gui_basics/widgets/juce_TextEditor.cpp index ca5743ea06..d122c62c34 100644 --- a/modules/juce_gui_basics/widgets/juce_TextEditor.cpp +++ b/modules/juce_gui_basics/widgets/juce_TextEditor.cpp @@ -278,7 +278,7 @@ struct TextEditor::Iterator Iterator (const TextEditor& ed) : sections (ed.sections), justification (ed.justification), - justificationWidth (ed.getJustificationWidth()), + bottomRight (ed.getMaximumWidth(), ed.getMaximumHeight()), wordWrapWidth (ed.getWordWrapWidth()), passwordCharacter (ed.passwordCharacter), lineSpacing (ed.lineSpacing) @@ -292,6 +292,8 @@ struct TextEditor::Iterator if (currentSection != nullptr) beginNewLine(); } + + lineHeight = ed.currentFont.getHeight(); } Iterator (const Iterator&) = default; @@ -325,7 +327,7 @@ struct TextEditor::Iterator { tempAtom.numChars = (uint16) split; tempAtom.width = g.getGlyph (split - 1).getRight(); - atomX = getJustificationOffset (tempAtom.width); + atomX = getJustificationOffsetX (tempAtom.width); atomRight = atomX + tempAtom.width; return true; } @@ -427,14 +429,14 @@ struct TextEditor::Iterator tempAtom.numChars = 0; atom = &tempAtom; - if (atomX > justificationOffset) + if (atomX > justificationOffsetX) beginNewLine(); return next(); } beginNewLine(); - atomX = justificationOffset; + atomX = justificationOffsetX; atomRight = atomX + atom->width; return true; } @@ -494,23 +496,20 @@ struct TextEditor::Iterator ++tempAtomIndex; } - justificationOffset = getJustificationOffset (lineWidth); - atomX = justificationOffset; + justificationOffsetX = getJustificationOffsetX (lineWidth); + atomX = justificationOffsetX; } - float getJustificationOffset (float lineWidth) const + float getJustificationOffsetX (float lineWidth) const { - if (justification.getOnlyHorizontalFlags() == Justification::horizontallyCentred) - return jmax (0.0f, (justificationWidth - lineWidth) * 0.5f); - - if (justification.getOnlyHorizontalFlags() == Justification::right) - return jmax (0.0f, justificationWidth - lineWidth); + if (justification.testFlags (Justification::horizontallyCentred)) return jmax (0.0f, (bottomRight.x - lineWidth) * 0.5f); + if (justification.testFlags (Justification::right)) return jmax (0.0f, bottomRight.x - lineWidth); return 0; } //============================================================================== - void draw (Graphics& g, const UniformTextSection*& lastSection) const + void draw (Graphics& g, const UniformTextSection*& lastSection, AffineTransform transform) const { if (passwordCharacter != 0 || ! atom->isWhitespace()) { @@ -527,7 +526,7 @@ struct TextEditor::Iterator ga.addLineOfText (currentSection->font, atom->getTrimmedText (passwordCharacter), atomX, (float) roundToInt (lineY + lineHeight - maxDescent)); - ga.draw (g); + ga.draw (g, transform); } } @@ -539,18 +538,19 @@ struct TextEditor::Iterator area.add (startX, lineY, endX - startX, lineHeight * lineSpacing); } - void drawUnderline (Graphics& g, Range underline, Colour colour) const + void drawUnderline (Graphics& g, Range underline, Colour colour, AffineTransform transform) const { auto startX = roundToInt (indexToX (underline.getStart())); auto endX = roundToInt (indexToX (underline.getEnd())); auto baselineY = roundToInt (lineY + currentSection->font.getAscent() + 0.5f); Graphics::ScopedSaveState state (g); + g.addTransform (transform); g.reduceClipRegion ({ startX, baselineY, endX - startX, 1 }); g.fillCheckerBoard ({ (float) endX, (float) baselineY + 1.0f }, 3.0f, 1.0f, colour, Colours::transparentBlack); } - void drawSelectedText (Graphics& g, Range selected, Colour selectedTextColour) const + void drawSelectedText (Graphics& g, Range selected, Colour selectedTextColour, AffineTransform transform) const { if (passwordCharacter != 0 || ! atom->isWhitespace()) { @@ -566,7 +566,7 @@ struct TextEditor::Iterator ga.removeRangeOfGlyphs (selected.getEnd() - indexInText, -1); g.setColour (currentSection->colour); - ga2.draw (g); + ga2.draw (g, transform); } if (selected.getStart() > indexInText) @@ -576,11 +576,11 @@ struct TextEditor::Iterator ga.removeRangeOfGlyphs (0, selected.getStart() - indexInText); g.setColour (currentSection->colour); - ga2.draw (g); + ga2.draw (g, transform); } g.setColour (selectedTextColour); - ga.draw (g); + ga.draw (g, transform); } } @@ -649,18 +649,39 @@ struct TextEditor::Iterator return false; } + float getYOffset() + { + if (justification.testFlags (Justification::top) || lineY >= bottomRight.y) + return 0; + + while (next()) + { + if (lineY >= bottomRight.y) + return 0; + } + + auto bottom = jmax (0.0f, bottomRight.y - lineY - lineHeight); + + if (justification.testFlags (Justification::bottom)) + return bottom; + + return bottom * 0.5f; + } + //============================================================================== int indexInText = 0; - float lineY = 0, justificationOffset = 0, lineHeight = 0, maxDescent = 0; + float lineY = 0, lineHeight = 0, maxDescent = 0; float atomX = 0, atomRight = 0; const TextAtom* atom = nullptr; - const UniformTextSection* currentSection = nullptr; private: const OwnedArray& sections; + const UniformTextSection* currentSection = nullptr; int sectionIndex = 0, atomIndex = 0; Justification justification; - const float justificationWidth, wordWrapWidth; + float justificationOffsetX = 0; + const Point bottomRight; + const float wordWrapWidth; const juce_wchar passwordCharacter; const float lineSpacing; TextAtom tempAtom; @@ -673,7 +694,7 @@ private: if (atom->isNewLine()) { - atomX = 0.0f; + atomX = getJustificationOffsetX (0); lineY += lineHeight * lineSpacing; } } @@ -826,8 +847,8 @@ struct TextEditor::TextEditorViewport : public Viewport void visibleAreaChanged (const Rectangle&) override { - if (! rentrant) // it's rare, but possible to get into a feedback loop as the viewport's scrollbars - // appear and disappear, causing the wrap width to change. + if (! reentrant) // it's rare, but possible to get into a feedback loop as the viewport's scrollbars + // appear and disappear, causing the wrap width to change. { auto wordWrapWidth = owner.getWordWrapWidth(); @@ -835,9 +856,8 @@ struct TextEditor::TextEditorViewport : public Viewport { lastWordWrapWidth = wordWrapWidth; - rentrant = true; - owner.updateTextHolderSize(); - rentrant = false; + ScopedValueSetter svs (reentrant, true); + owner.checkLayout(); } } } @@ -845,7 +865,7 @@ struct TextEditor::TextEditorViewport : public Viewport private: TextEditor& owner; float lastWordWrapWidth = 0; - bool rentrant = false; + bool reentrant = false; JUCE_DECLARE_NON_COPYABLE (TextEditorViewport) }; @@ -936,8 +956,8 @@ void TextEditor::setMultiLine (const bool shouldBeMultiLine, multiline = shouldBeMultiLine; wordWrap = shouldWordWrap && shouldBeMultiLine; - viewport->setScrollBarsShown (scrollbarVisible && multiline, - scrollbarVisible && multiline); + checkLayout(); + viewport->setViewPosition (0, 0); resized(); scrollToMakeSureCursorIsVisible(); @@ -954,8 +974,7 @@ void TextEditor::setScrollbarsShown (bool shown) if (scrollbarVisible != shown) { scrollbarVisible = shown; - shown = shown && isMultiLine(); - viewport->setScrollBarsShown (shown, shown); + checkLayout(); } } @@ -1003,7 +1022,9 @@ void TextEditor::setJustification (Justification j) if (justification != j) { justification = j; + resized(); + repaint(); } } @@ -1028,7 +1049,7 @@ void TextEditor::applyFontToAllText (const Font& newFont, bool changeCurrentFont } coalesceSimilarSections(); - updateTextHolderSize(); + checkLayout(); scrollToMakeSureCursorIsVisible(); repaint(); } @@ -1090,8 +1111,13 @@ void TextEditor::recreateCaret() void TextEditor::updateCaretPosition() { - if (caret != nullptr) - caret->setCaretPosition (getCaretRectangle().translated (leftIndent, topIndent)); + if (caret != nullptr + && getWidth() > 0 && getHeight() > 0) + { + Iterator i (*this); + caret->setCaretPosition (getCaretRectangle().translated (leftIndent, + topIndent + roundToInt (i.getYOffset()))); + } } TextEditor::LengthAndCharacterRestriction::LengthAndCharacterRestriction (int maxLen, const String& chars) @@ -1146,7 +1172,7 @@ void TextEditor::setScrollBarThickness (int newThicknessPixels) void TextEditor::clear() { clearInternal (nullptr); - updateTextHolderSize(); + checkLayout(); undoManager.clearUndoHistory(); } @@ -1181,7 +1207,7 @@ void TextEditor::setText (const String& newText, bool sendTextChangeMessage) else textValue.addListener (textHolder); - updateTextHolderSize(); + checkLayout(); scrollToMakeSureCursorIsVisible(); undoManager.clearUndoHistory(); @@ -1214,7 +1240,7 @@ void TextEditor::textWasChangedByValue() //============================================================================== void TextEditor::textChanged() { - updateTextHolderSize(); + checkLayout(); if (listeners.size() != 0 || onTextChange != nullptr) postCommandMessage (TextEditorDefs::textChangeMessageId); @@ -1259,30 +1285,33 @@ void TextEditor::repaintText (Range range) { if (! range.isEmpty()) { - auto lh = currentFont.getHeight(); - auto wordWrapWidth = getWordWrapWidth(); - - if (wordWrapWidth > 0) + if (range.getEnd() >= getTotalNumChars()) { - Point anchor; - Iterator i (*this); - i.getCharPosition (range.getStart(), anchor, lh); + textHolder->repaint(); + return; + } - auto y1 = (int) anchor.y; - int y2; + Iterator i (*this); - if (range.getEnd() >= getTotalNumChars()) - { - y2 = textHolder->getHeight(); - } - else - { - i.getCharPosition (range.getEnd(), anchor, lh); - y2 = (int) (anchor.y + lh * 2.0f); - } + Point anchor; + auto lh = currentFont.getHeight(); + i.getCharPosition (range.getStart(), anchor, lh); + + auto y1 = (int) anchor.y; + int y2; - textHolder->repaint (0, y1, textHolder->getWidth(), y2 - y1); + if (range.getEnd() >= getTotalNumChars()) + { + y2 = textHolder->getHeight(); } + else + { + i.getCharPosition (range.getEnd(), anchor, lh); + y2 = (int) (anchor.y + lh * 2.0f); + } + + auto offset = i.getYOffset(); + textHolder->repaint (0, roundToInt (y1 + offset), textHolder->getWidth(), roundToInt (y2 - y1 + offset)); } } @@ -1367,33 +1396,66 @@ Rectangle TextEditor::getCaretRectangleFloat() const } //============================================================================== -enum { rightEdgeSpace = 2 }; +// Extra space for the cursor at the right-hand-edge +constexpr int rightEdgeSpace = 2; float TextEditor::getWordWrapWidth() const { - return wordWrap ? getJustificationWidth() + return wordWrap ? getMaximumWidth() : std::numeric_limits::max(); } -float TextEditor::getJustificationWidth() const +float TextEditor::getMaximumWidth() const { return (float) (viewport->getMaximumVisibleWidth() - (leftIndent + rightEdgeSpace + 1)); } -void TextEditor::updateTextHolderSize() +float TextEditor::getMaximumHeight() const +{ + return (float) (viewport->getMaximumVisibleHeight() - topIndent); +} + +void TextEditor::checkLayout() { if (getWordWrapWidth() > 0) { - float maxWidth = getJustificationWidth(); + auto maxWidth = getMaximumWidth(); Iterator i (*this); while (i.next()) maxWidth = jmax (maxWidth, i.atomRight); - auto w = leftIndent + roundToInt (maxWidth); - auto h = topIndent + roundToInt (jmax (i.lineY + i.lineHeight, currentFont.getHeight())); + auto textRight = roundToInt (maxWidth); + auto textBottom = roundToInt (i.lineY + i.lineHeight + i.getYOffset()); + + if (i.atom != nullptr && i.atom->isNewLine()) + textBottom += (int) i.lineHeight; - textHolder->setSize (w + rightEdgeSpace, h + 1); // (allows a bit of space for the cursor to be at the right-hand-edge) + updateTextHolderSize (textRight, textBottom); + updateScrollbarVisibility (textRight, textBottom); + } +} + +void TextEditor::updateTextHolderSize (int textRight, int textBottom) +{ + auto w = leftIndent + jmax (roundToInt (getMaximumWidth()), textRight); + auto h = topIndent + textBottom; + + textHolder->setSize (w + rightEdgeSpace, h + 1); +} + +void TextEditor::updateScrollbarVisibility (int textRight, int textBottom) +{ + if (scrollbarVisible && multiline) + { + auto horizontalVisible = (leftIndent + textRight) > (viewport->getMaximumVisibleWidth() - viewport->getScrollBarThickness()); + auto verticalVisible = (topIndent + textBottom) > (viewport->getMaximumVisibleHeight() + 1); + + viewport->setScrollBarsShown (verticalVisible, horizontalVisible); + } + else + { + viewport->setScrollBarsShown (false, false); } } @@ -1402,8 +1464,14 @@ int TextEditor::getTextHeight() const { return textHolder->getHeight(); } void TextEditor::setIndents (int newLeftIndent, int newTopIndent) { - leftIndent = newLeftIndent; - topIndent = newTopIndent; + if (leftIndent != newLeftIndent || topIndent != newTopIndent) + { + leftIndent = newLeftIndent; + topIndent = newTopIndent; + + resized(); + repaint(); + } } void TextEditor::setBorder (BorderSize border) @@ -1506,8 +1574,10 @@ void TextEditor::moveCaretTo (const int newPosition, const bool isSelecting) int TextEditor::getTextIndexAt (const int x, const int y) { + Iterator i (*this); + return indexAtPosition ((float) (x + viewport->getViewPositionX() - leftIndent - borderSize.getLeft()), - (float) (y + viewport->getViewPositionY() - topIndent - borderSize.getTop())); + (float) (y + viewport->getViewPositionY() - topIndent - borderSize.getTop() - i.getYOffset())); } void TextEditor::insertTextAtCaret (const String& t) @@ -1576,8 +1646,23 @@ void TextEditor::drawContent (Graphics& g) { g.setOrigin (leftIndent, topIndent); auto clip = g.getClipBounds(); - Colour selectedTextColour; + + auto yOffset = [this]() + { + Iterator i (*this); + return i.getYOffset(); + }(); + + AffineTransform transform; + + if (yOffset > 0) + { + transform = AffineTransform::translation (0.0f, yOffset); + clip.setY (roundToInt (clip.getY() - yOffset)); + } + Iterator i (*this); + Colour selectedTextColour; if (! selection.isEmpty()) { @@ -1593,10 +1678,10 @@ void TextEditor::drawContent (Graphics& g) } } - g.setColour (findColour (highlightColourId).withMultipliedAlpha (hasKeyboardFocus (true) ? 1.0f : 0.5f)); - g.fillRectList (selectionArea); - selectedTextColour = findColour (highlightedTextColourId); + + g.setColour (findColour (highlightColourId).withMultipliedAlpha (hasKeyboardFocus (true) ? 1.0f : 0.5f)); + g.fillPath (selectionArea.toPath(), transform); } const UniformTextSection* lastSection = nullptr; @@ -1607,12 +1692,12 @@ void TextEditor::drawContent (Graphics& g) { if (selection.intersects ({ i.indexInText, i.indexInText + i.atom->numChars })) { - i.drawSelectedText (g, selection, selectedTextColour); + i.drawSelectedText (g, selection, selectedTextColour, transform); lastSection = nullptr; } else { - i.draw (g, lastSection); + i.draw (g, lastSection, transform); } } } @@ -1626,7 +1711,7 @@ void TextEditor::drawContent (Graphics& g) if (i2.lineY + i2.lineHeight >= (float) clip.getY() && underlinedSection.intersects ({ i2.indexInText, i2.indexInText + i2.atom->numChars })) { - i2.drawUnderline (g, underlinedSection, findColour (textColourId)); + i2.drawUnderline (g, underlinedSection, findColour (textColourId), transform); } } } @@ -1647,13 +1732,10 @@ void TextEditor::paintOverChildren (Graphics& g) g.setColour (colourForTextWhenEmpty); g.setFont (getFont()); - if (isMultiLine()) - g.drawText (textToShowWhenEmpty, getLocalBounds(), - Justification::centred, true); - else - g.drawText (textToShowWhenEmpty, - leftIndent, 0, viewport->getWidth() - leftIndent, getHeight(), - Justification::centredLeft, true); + g.drawText (textToShowWhenEmpty, + leftIndent, topIndent, + viewport->getWidth() - leftIndent, getHeight() - topIndent, + justification, true); } getLookAndFeel().drawTextEditorOutline (g, getWidth(), getHeight(), *this); @@ -2095,7 +2177,7 @@ void TextEditor::resized() viewport->setBoundsInset (borderSize); viewport->setSingleStepSizes (16, roundToInt (currentFont.getHeight())); - updateTextHolderSize(); + checkLayout(); if (isMultiLine()) updateCaretPosition(); @@ -2213,7 +2295,7 @@ void TextEditor::insert (const String& text, int insertIndex, const Font& font, totalNumChars = -1; valueTextNeedsUpdating = true; - updateTextHolderSize(); + checkLayout(); moveCaretTo (caretPositionToMoveTo, false); repaintText ({ insertIndex, getTotalNumChars() }); @@ -2426,7 +2508,7 @@ void TextEditor::getCharPosition (int index, Point& anchor, float& lineHe if (sections.isEmpty()) { - anchor = { i.getJustificationOffset (0), 0 }; + anchor = { i.getJustificationOffsetX (0), 0 }; lineHeight = currentFont.getHeight(); } else diff --git a/modules/juce_gui_basics/widgets/juce_TextEditor.h b/modules/juce_gui_basics/widgets/juce_TextEditor.h index 6107158d0d..aa57e5766d 100644 --- a/modules/juce_gui_basics/widgets/juce_TextEditor.h +++ b/modules/juce_gui_basics/widgets/juce_TextEditor.h @@ -155,7 +155,6 @@ public: */ bool areScrollbarsShown() const noexcept { return scrollbarVisible; } - /** Changes the password character used to disguise the text. @param passwordCharacter if this is not zero, this character will be used as a replacement @@ -172,7 +171,6 @@ public: */ juce_wchar getPasswordCharacter() const noexcept { return passwordCharacter; } - //============================================================================== /** Allows a right-click menu to appear for the editor. @@ -495,9 +493,12 @@ public: */ void setScrollToShowCursor (bool shouldScrollToShowCaret); - /** Modifies the horizontal justification of the text within the editor window. */ + /** Modifies the justification of the text within the editor window. */ void setJustification (Justification newJustification); + /** Returns the type of justification, as set in setJustification(). */ + Justification getJustificationType() const noexcept { return justification; } + /** Sets the line spacing of the TextEditor. The default (and minimum) value is 1.0 and values > 1.0 will increase the line spacing as a multiple of the line height e.g. for double-spacing call this method with an argument of 2.0. @@ -712,7 +713,7 @@ private: std::unique_ptr viewport; TextHolderComponent* textHolder; BorderSize borderSize { 1, 1, 1, 3 }; - Justification justification { Justification::left }; + Justification justification { Justification::topLeft }; bool readOnly = false; bool caretVisible = true; @@ -778,9 +779,12 @@ private: int findWordBreakBefore (int position) const; bool moveCaretWithTransaction (int newPos, bool selecting); void drawContent (Graphics&); - void updateTextHolderSize(); + void checkLayout(); + void updateTextHolderSize (int, int); + void updateScrollbarVisibility (int, int); float getWordWrapWidth() const; - float getJustificationWidth() const; + float getMaximumWidth() const; + float getMaximumHeight() const; void timerCallbackInt(); void checkFocus(); void repaintText (Range);