Browse Source

Added a class TextEditor::InputFilter to perform custom filters on text input.

tags/2021-05-28
jules 12 years ago
parent
commit
a180c6c358
2 changed files with 171 additions and 178 deletions
  1. +105
    -160
      modules/juce_gui_basics/widgets/juce_TextEditor.cpp
  2. +66
    -18
      modules/juce_gui_basics/widgets/juce_TextEditor.h

+ 105
- 160
modules/juce_gui_basics/widgets/juce_TextEditor.cpp View File

@@ -67,7 +67,7 @@ public:
} }
UniformTextSection (const UniformTextSection& other) UniformTextSection (const UniformTextSection& other)
: font (other.font), colour (other.colour)
: font (other.font), colour (other.colour)
{ {
atoms.ensureStorageAllocated (other.atoms.size()); atoms.ensureStorageAllocated (other.atoms.size());
@@ -80,39 +80,28 @@ public:
void clear() void clear()
{ {
for (int i = atoms.size(); --i >= 0;) for (int i = atoms.size(); --i >= 0;)
delete getAtom(i);
delete atoms.getUnchecked (i);
atoms.clear(); atoms.clear();
} }
int getNumAtoms() const
{
return atoms.size();
}
TextAtom* getAtom (const int index) const noexcept
{
return atoms.getUnchecked (index);
}
void append (const UniformTextSection& other, const juce_wchar passwordCharacter)
void append (const UniformTextSection& other, const juce_wchar passwordChar)
{ {
if (other.atoms.size() > 0) if (other.atoms.size() > 0)
{ {
TextAtom* const lastAtom = atoms.getLast();
int i = 0; int i = 0;
if (lastAtom != nullptr)
if (TextAtom* const lastAtom = atoms.getLast())
{ {
if (! CharacterFunctions::isWhitespace (lastAtom->atomText.getLastCharacter())) if (! CharacterFunctions::isWhitespace (lastAtom->atomText.getLastCharacter()))
{ {
TextAtom* const first = other.getAtom(0);
TextAtom* const first = other.atoms.getUnchecked(0);
if (! CharacterFunctions::isWhitespace (first->atomText[0])) if (! CharacterFunctions::isWhitespace (first->atomText[0]))
{ {
lastAtom->atomText += first->atomText; lastAtom->atomText += first->atomText;
lastAtom->numChars = (uint16) (lastAtom->numChars + first->numChars); lastAtom->numChars = (uint16) (lastAtom->numChars + first->numChars);
lastAtom->width = font.getStringWidthFloat (lastAtom->getText (passwordCharacter));
lastAtom->width = font.getStringWidthFloat (lastAtom->getText (passwordChar));
delete first; delete first;
++i; ++i;
} }
@@ -123,30 +112,27 @@ public:
while (i < other.atoms.size()) while (i < other.atoms.size())
{ {
atoms.add (other.getAtom(i));
atoms.add (other.atoms.getUnchecked(i));
++i; ++i;
} }
} }
} }
UniformTextSection* split (const int indexToBreakAt,
const juce_wchar passwordCharacter)
UniformTextSection* split (const int indexToBreakAt, const juce_wchar passwordChar)
{ {
UniformTextSection* const section2 = new UniformTextSection (String::empty,
font, colour,
passwordCharacter);
UniformTextSection* const section2 = new UniformTextSection (String::empty, font, colour, passwordChar);
int index = 0; int index = 0;
for (int i = 0; i < atoms.size(); ++i) for (int i = 0; i < atoms.size(); ++i)
{ {
TextAtom* const atom = getAtom(i);
TextAtom* const atom = atoms.getUnchecked(i);
const int nextIndex = index + atom->numChars; const int nextIndex = index + atom->numChars;
if (index == indexToBreakAt) if (index == indexToBreakAt)
{ {
for (int j = i; j < atoms.size(); ++j) for (int j = i; j < atoms.size(); ++j)
section2->atoms.add (getAtom (j));
section2->atoms.add (atoms.getUnchecked (j));
for (int j = atoms.size(); --j >= i;) for (int j = atoms.size(); --j >= i;)
atoms.remove (j); atoms.remove (j);
@@ -158,17 +144,17 @@ public:
TextAtom* const secondAtom = new TextAtom(); TextAtom* const secondAtom = new TextAtom();
secondAtom->atomText = atom->atomText.substring (indexToBreakAt - index); secondAtom->atomText = atom->atomText.substring (indexToBreakAt - index);
secondAtom->width = font.getStringWidthFloat (secondAtom->getText (passwordCharacter));
secondAtom->width = font.getStringWidthFloat (secondAtom->getText (passwordChar));
secondAtom->numChars = (uint16) secondAtom->atomText.length(); secondAtom->numChars = (uint16) secondAtom->atomText.length();
section2->atoms.add (secondAtom); section2->atoms.add (secondAtom);
atom->atomText = atom->atomText.substring (0, indexToBreakAt - index); atom->atomText = atom->atomText.substring (0, indexToBreakAt - index);
atom->width = font.getStringWidthFloat (atom->getText (passwordCharacter));
atom->width = font.getStringWidthFloat (atom->getText (passwordChar));
atom->numChars = (uint16) (indexToBreakAt - index); atom->numChars = (uint16) (indexToBreakAt - index);
for (int j = i + 1; j < atoms.size(); ++j) for (int j = i + 1; j < atoms.size(); ++j)
section2->atoms.add (getAtom (j));
section2->atoms.add (atoms.getUnchecked (j));
for (int j = atoms.size(); --j > i;) for (int j = atoms.size(); --j > i;)
atoms.remove (j); atoms.remove (j);
@@ -185,7 +171,7 @@ public:
void appendAllText (MemoryOutputStream& mo) const void appendAllText (MemoryOutputStream& mo) const
{ {
for (int i = 0; i < atoms.size(); ++i) for (int i = 0; i < atoms.size(); ++i)
mo << getAtom(i)->atomText;
mo << atoms.getUnchecked(i)->atomText;
} }
void appendSubstring (MemoryOutputStream& mo, const Range<int>& range) const void appendSubstring (MemoryOutputStream& mo, const Range<int>& range) const
@@ -193,7 +179,7 @@ public:
int index = 0; int index = 0;
for (int i = 0; i < atoms.size(); ++i) for (int i = 0; i < atoms.size(); ++i)
{ {
const TextAtom* const atom = getAtom (i);
const TextAtom* const atom = atoms.getUnchecked (i);
const int nextIndex = index + atom->numChars; const int nextIndex = index + atom->numChars;
if (range.getStart() < nextIndex) if (range.getStart() < nextIndex)
@@ -211,18 +197,17 @@ public:
} }
} }
int getTotalLength() const
int getTotalLength() const noexcept
{ {
int total = 0; int total = 0;
for (int i = atoms.size(); --i >= 0;) for (int i = atoms.size(); --i >= 0;)
total += getAtom(i)->numChars;
total += atoms.getUnchecked(i)->numChars;
return total; return total;
} }
void setFont (const Font& newFont,
const juce_wchar passwordCharacter)
void setFont (const Font& newFont, const juce_wchar passwordChar)
{ {
if (font != newFont) if (font != newFont)
{ {
@@ -231,7 +216,7 @@ public:
for (int i = atoms.size(); --i >= 0;) for (int i = atoms.size(); --i >= 0;)
{ {
TextAtom* const atom = atoms.getUnchecked(i); TextAtom* const atom = atoms.getUnchecked(i);
atom->width = newFont.getStringWidthFloat (atom->getText (passwordCharacter));
atom->width = newFont.getStringWidthFloat (atom->getText (passwordChar));
} }
} }
} }
@@ -239,13 +224,10 @@ public:
//============================================================================== //==============================================================================
Font font; Font font;
Colour colour; Colour colour;
private:
Array <TextAtom*> atoms; Array <TextAtom*> atoms;
//==============================================================================
void initialiseAtoms (const String& textToParse,
const juce_wchar passwordCharacter)
private:
void initialiseAtoms (const String& textToParse, const juce_wchar passwordChar)
{ {
String::CharPointerType text (textToParse.getCharPointer()); String::CharPointerType text (textToParse.getCharPointer());
@@ -294,8 +276,7 @@ private:
TextAtom* const atom = new TextAtom(); TextAtom* const atom = new TextAtom();
atom->atomText = String (start, numChars); atom->atomText = String (start, numChars);
atom->width = font.getStringWidthFloat (atom->getText (passwordCharacter));
atom->width = font.getStringWidthFloat (atom->getText (passwordChar));
atom->numChars = (uint16) numChars; atom->numChars = (uint16) numChars;
atoms.add (atom); atoms.add (atom);
@@ -399,9 +380,9 @@ public:
moveToEndOfLastAtom(); moveToEndOfLastAtom();
return false; return false;
} }
else if (atomIndex >= currentSection->getNumAtoms() - 1)
else if (atomIndex >= currentSection->atoms.size() - 1)
{ {
if (atomIndex >= currentSection->getNumAtoms())
if (atomIndex >= currentSection->atoms.size())
{ {
if (++sectionIndex >= sections.size()) if (++sectionIndex >= sections.size())
{ {
@@ -414,7 +395,7 @@ public:
} }
else else
{ {
const TextAtom* const lastAtom = currentSection->getAtom (atomIndex);
const TextAtom* const lastAtom = currentSection->atoms.getUnchecked (atomIndex);
if (! lastAtom->isWhitespace()) if (! lastAtom->isWhitespace())
{ {
@@ -428,10 +409,10 @@ public:
{ {
const UniformTextSection* const s = sections.getUnchecked (section); const UniformTextSection* const s = sections.getUnchecked (section);
if (s->getNumAtoms() == 0)
if (s->atoms.size() == 0)
break; break;
const TextAtom* const nextAtom = s->getAtom (0);
const TextAtom* const nextAtom = s->atoms.getUnchecked (0);
if (nextAtom->isWhitespace()) if (nextAtom->isWhitespace())
break; break;
@@ -450,7 +431,7 @@ public:
break; break;
} }
if (s->getNumAtoms() > 1)
if (s->atoms.size() > 1)
break; break;
} }
} }
@@ -466,7 +447,7 @@ public:
beginNewLine(); beginNewLine();
} }
atom = currentSection->getAtom (atomIndex);
atom = currentSection->atoms.getUnchecked (atomIndex);
atomRight = atomX + atom->width; atomRight = atomX + atom->width;
++atomIndex; ++atomIndex;
@@ -523,7 +504,7 @@ public:
bool checkSize = false; bool checkSize = false;
if (tempAtomIndex >= section->getNumAtoms())
if (tempAtomIndex >= section->atoms.size())
{ {
if (++tempSectionIndex >= sections.size()) if (++tempSectionIndex >= sections.size())
break; break;
@@ -533,7 +514,7 @@ public:
checkSize = true; checkSize = true;
} }
const TextAtom* const nextAtom = section->getAtom (tempAtomIndex);
const TextAtom* const nextAtom = section->atoms.getUnchecked (tempAtomIndex);
if (nextAtom == nullptr) if (nextAtom == nullptr)
break; break;
@@ -570,16 +551,15 @@ public:
GlyphArrangement ga; GlyphArrangement ga;
ga.addLineOfText (currentSection->font, ga.addLineOfText (currentSection->font,
atom->getTrimmedText (passwordCharacter), atom->getTrimmedText (passwordCharacter),
atomX,
(float) roundToInt (lineY + lineHeight - maxDescent));
atomX, (float) roundToInt (lineY + lineHeight - maxDescent));
ga.draw (g); ga.draw (g);
} }
} }
void drawSelection (Graphics& g, const Range<int>& selection) const
void drawSelection (Graphics& g, const Range<int>& selected) const
{ {
const int startX = roundToInt (indexToX (selection.getStart()));
const int endX = roundToInt (indexToX (selection.getEnd()));
const int startX = roundToInt (indexToX (selected.getStart()));
const int endX = roundToInt (indexToX (selected.getEnd()));
const int y = roundToInt (lineY); const int y = roundToInt (lineY);
const int nextY = roundToInt (lineY + lineHeight); const int nextY = roundToInt (lineY + lineHeight);
@@ -599,7 +579,7 @@ public:
} }
void drawSelectedText (Graphics& g, void drawSelectedText (Graphics& g,
const Range<int>& selection,
const Range<int>& selected,
const Colour& selectedTextColour) const const Colour& selectedTextColour) const
{ {
if (passwordCharacter != 0 || ! atom->isWhitespace()) if (passwordCharacter != 0 || ! atom->isWhitespace())
@@ -607,24 +587,23 @@ public:
GlyphArrangement ga; GlyphArrangement ga;
ga.addLineOfText (currentSection->font, ga.addLineOfText (currentSection->font,
atom->getTrimmedText (passwordCharacter), atom->getTrimmedText (passwordCharacter),
atomX,
(float) roundToInt (lineY + lineHeight - maxDescent));
atomX, (float) roundToInt (lineY + lineHeight - maxDescent));
if (selection.getEnd() < indexInText + atom->numChars)
if (selected.getEnd() < indexInText + atom->numChars)
{ {
GlyphArrangement ga2 (ga); GlyphArrangement ga2 (ga);
ga2.removeRangeOfGlyphs (0, selection.getEnd() - indexInText);
ga.removeRangeOfGlyphs (selection.getEnd() - indexInText, -1);
ga2.removeRangeOfGlyphs (0, selected.getEnd() - indexInText);
ga.removeRangeOfGlyphs (selected.getEnd() - indexInText, -1);
g.setColour (currentSection->colour); g.setColour (currentSection->colour);
ga2.draw (g); ga2.draw (g);
} }
if (selection.getStart() > indexInText)
if (selected.getStart() > indexInText)
{ {
GlyphArrangement ga2 (ga); GlyphArrangement ga2 (ga);
ga2.removeRangeOfGlyphs (selection.getStart() - indexInText, -1);
ga.removeRangeOfGlyphs (0, selection.getStart() - indexInText);
ga2.removeRangeOfGlyphs (selected.getStart() - indexInText, -1);
ga.removeRangeOfGlyphs (0, selected.getStart() - indexInText);
g.setColour (currentSection->colour); g.setColour (currentSection->colour);
ga2.draw (g); ga2.draw (g);
@@ -726,7 +705,7 @@ private:
} }
} }
bool shouldWrap (const float x) const
bool shouldWrap (const float x) const noexcept
{ {
return (x - 0.0001f) >= wordWrapWidth; return (x - 0.0001f) >= wordWrapWidth;
} }
@@ -824,12 +803,11 @@ public:
int getSizeInUnits() int getSizeInUnits()
{ {
int n = 0;
int n = 16;
for (int i = removedSections.size(); --i >= 0;) for (int i = removedSections.size(); --i >= 0;)
n += removedSections.getUnchecked (i)->getTotalLength(); n += removedSections.getUnchecked (i)->getTotalLength();
return n + 16;
return n;
} }
private: private:
@@ -957,7 +935,6 @@ TextEditor::TextEditor (const String& name,
menuActive (false), menuActive (false),
valueTextNeedsUpdating (false), valueTextNeedsUpdating (false),
consumeEscAndReturnKeys (true), consumeEscAndReturnKeys (true),
maxTextLength (0),
leftIndent (4), leftIndent (4),
topIndent (4), topIndent (4),
lastTransactionTime (0), lastTransactionTime (0),
@@ -982,11 +959,8 @@ TextEditor::TextEditor (const String& name,
TextEditor::~TextEditor() TextEditor::~TextEditor()
{ {
if (wasFocused) if (wasFocused)
{
ComponentPeer* const peer = getPeer();
if (peer != nullptr)
if (ComponentPeer* const peer = getPeer())
peer->dismissPendingTextInput(); peer->dismissPendingTextInput();
}
textValue.referTo (Value()); textValue.referTo (Value());
clearInternal (0); clearInternal (0);
@@ -1105,7 +1079,6 @@ void TextEditor::setFont (const Font& newFont)
void TextEditor::applyFontToAllText (const Font& newFont) void TextEditor::applyFontToAllText (const Font& newFont)
{ {
currentFont = newFont; currentFont = newFont;
const Colour overallColour (findColour (textColourId)); const Colour overallColour (findColour (textColourId));
for (int i = sections.size(); --i >= 0;) for (int i = sections.size(); --i >= 0;)
@@ -1155,11 +1128,32 @@ void TextEditor::updateCaretPosition()
caret->setCaretPosition (getCaretRectangle().translated (leftIndent, topIndent)); caret->setCaretPosition (getCaretRectangle().translated (leftIndent, topIndent));
} }
TextEditor::LengthAndCharacterRestriction::LengthAndCharacterRestriction (int maxLen, const String& chars)
: allowedCharacters (chars), maxLength (maxLen)
{}
String TextEditor::LengthAndCharacterRestriction::filterNewText (TextEditor& ed, const String& newInput)
{
String t (newInput);
if (allowedCharacters.isNotEmpty())
t = t.retainCharacters (allowedCharacters);
if (maxLength > 0)
t = t.substring (0, maxLength - (ed.getTotalNumChars() - ed.getHighlightedRegion().getLength()));
return t;
}
void TextEditor::setInputFilter (InputFilter* newFilter, bool takeOwnership)
{
inputFilter.set (newFilter, takeOwnership);
}
void TextEditor::setInputRestrictions (const int maxLen, void TextEditor::setInputRestrictions (const int maxLen,
const String& chars) const String& chars)
{ {
maxTextLength = jmax (0, maxLen);
allowedCharacters = chars;
setInputFilter (new LengthAndCharacterRestriction (maxLen, chars), true);
} }
void TextEditor::setTextToShowWhenEmpty (const String& text, const Colour& colourToUse) void TextEditor::setTextToShowWhenEmpty (const String& text, const Colour& colourToUse)
@@ -1262,25 +1256,11 @@ void TextEditor::textChanged()
} }
} }
void TextEditor::returnPressed()
{
postCommandMessage (TextEditorDefs::returnKeyMessageId);
}
void TextEditor::escapePressed()
{
postCommandMessage (TextEditorDefs::escapeKeyMessageId);
}
void TextEditor::addListener (TextEditorListener* const newListener)
{
listeners.add (newListener);
}
void TextEditor::returnPressed() { postCommandMessage (TextEditorDefs::returnKeyMessageId); }
void TextEditor::escapePressed() { postCommandMessage (TextEditorDefs::escapeKeyMessageId); }
void TextEditor::removeListener (TextEditorListener* const listenerToRemove)
{
listeners.remove (listenerToRemove);
}
void TextEditor::addListener (TextEditorListener* const l) { listeners.add (l); }
void TextEditor::removeListener (TextEditorListener* const l) { listeners.remove (l); }
//============================================================================== //==============================================================================
void TextEditor::timerCallbackInt() void TextEditor::timerCallbackInt()
@@ -1430,21 +1410,13 @@ void TextEditor::updateTextHolderSize()
} }
} }
int TextEditor::getTextWidth() const
{
return textHolder->getWidth();
}
int TextEditor::getTextHeight() const
{
return textHolder->getHeight();
}
int TextEditor::getTextWidth() const { return textHolder->getWidth(); }
int TextEditor::getTextHeight() const { return textHolder->getHeight(); }
void TextEditor::setIndents (const int newLeftIndent,
const int newTopIndent)
void TextEditor::setIndents (const int newLeftIndent, const int newTopIndent)
{ {
leftIndent = newLeftIndent; leftIndent = newLeftIndent;
topIndent = newTopIndent;
topIndent = newTopIndent;
} }
void TextEditor::setBorder (const BorderSize<int>& border) void TextEditor::setBorder (const BorderSize<int>& border)
@@ -1561,32 +1533,21 @@ int TextEditor::getTextIndexAt (const int x, const int y)
void TextEditor::insertTextAtCaret (const String& t) void TextEditor::insertTextAtCaret (const String& t)
{ {
String newText (t);
if (allowedCharacters.isNotEmpty())
newText = newText.retainCharacters (allowedCharacters);
String newText (inputFilter != nullptr ? inputFilter->filterNewText (*this, t) : t);
if (! isMultiLine())
newText = newText.replaceCharacters ("\r\n", " ");
else
if (isMultiLine())
newText = newText.replace ("\r\n", "\n"); newText = newText.replace ("\r\n", "\n");
else
newText = newText.replaceCharacters ("\r\n", " ");
const int newCaretPos = selection.getStart() + newText.length();
const int insertIndex = selection.getStart(); const int insertIndex = selection.getStart();
const int newCaretPos = insertIndex + newText.length();
remove (selection, getUndoManager(), remove (selection, getUndoManager(),
newText.isNotEmpty() ? newCaretPos - 1 : newCaretPos); newText.isNotEmpty() ? newCaretPos - 1 : newCaretPos);
if (maxTextLength > 0)
newText = newText.substring (0, maxTextLength - getTotalNumChars());
if (newText.isNotEmpty())
insert (newText,
insertIndex,
currentFont,
findColour (textColourId),
getUndoManager(),
newCaretPos);
insert (newText, insertIndex, currentFont, findColour (textColourId),
getUndoManager(), newCaretPos);
textChanged(); textChanged();
} }
@@ -1714,16 +1675,12 @@ void TextEditor::paintOverChildren (Graphics& g)
g.setFont (getFont()); g.setFont (getFont());
if (isMultiLine()) if (isMultiLine())
{
g.drawText (textToShowWhenEmpty, getLocalBounds(), g.drawText (textToShowWhenEmpty, getLocalBounds(),
Justification::centred, true); Justification::centred, true);
}
else else
{
g.drawText (textToShowWhenEmpty, g.drawText (textToShowWhenEmpty,
leftIndent, 0, viewport->getWidth() - leftIndent, getHeight(), leftIndent, 0, viewport->getWidth() - leftIndent, getHeight(),
Justification::centredLeft, true); Justification::centredLeft, true);
}
} }
getLookAndFeel().drawTextEditorOutline (g, getWidth(), getHeight(), *this); getLookAndFeel().drawTextEditorOutline (g, getWidth(), getHeight(), *this);
@@ -1888,7 +1845,7 @@ void TextEditor::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& w
} }
//============================================================================== //==============================================================================
bool TextEditor::moveCaretWithTransation (const int newPos, const bool selecting)
bool TextEditor::moveCaretWithTransaction (const int newPos, const bool selecting)
{ {
newTransaction(); newTransaction();
moveCaretTo (newPos, selecting); moveCaretTo (newPos, selecting);
@@ -1904,7 +1861,7 @@ bool TextEditor::moveCaretLeft (bool moveInWholeWordSteps, bool selecting)
else else
--pos; --pos;
return moveCaretWithTransation (pos, selecting);
return moveCaretWithTransaction (pos, selecting);
} }
bool TextEditor::moveCaretRight (bool moveInWholeWordSteps, bool selecting) bool TextEditor::moveCaretRight (bool moveInWholeWordSteps, bool selecting)
@@ -1916,7 +1873,7 @@ bool TextEditor::moveCaretRight (bool moveInWholeWordSteps, bool selecting)
else else
++pos; ++pos;
return moveCaretWithTransation (pos, selecting);
return moveCaretWithTransaction (pos, selecting);
} }
bool TextEditor::moveCaretUp (bool selecting) bool TextEditor::moveCaretUp (bool selecting)
@@ -1925,7 +1882,7 @@ bool TextEditor::moveCaretUp (bool selecting)
return moveCaretToStartOfLine (selecting); return moveCaretToStartOfLine (selecting);
const Rectangle<float> caretPos (getCaretRectangle().toFloat()); const Rectangle<float> caretPos (getCaretRectangle().toFloat());
return moveCaretWithTransation (indexAtPosition (caretPos.getX(), caretPos.getY() - 1.0f), selecting);
return moveCaretWithTransaction (indexAtPosition (caretPos.getX(), caretPos.getY() - 1.0f), selecting);
} }
bool TextEditor::moveCaretDown (bool selecting) bool TextEditor::moveCaretDown (bool selecting)
@@ -1934,7 +1891,7 @@ bool TextEditor::moveCaretDown (bool selecting)
return moveCaretToEndOfLine (selecting); return moveCaretToEndOfLine (selecting);
const Rectangle<float> caretPos (getCaretRectangle().toFloat()); const Rectangle<float> caretPos (getCaretRectangle().toFloat());
return moveCaretWithTransation (indexAtPosition (caretPos.getX(), caretPos.getBottom() + 1.0f), selecting);
return moveCaretWithTransaction (indexAtPosition (caretPos.getX(), caretPos.getBottom() + 1.0f), selecting);
} }
bool TextEditor::pageUp (bool selecting) bool TextEditor::pageUp (bool selecting)
@@ -1943,7 +1900,7 @@ bool TextEditor::pageUp (bool selecting)
return moveCaretToStartOfLine (selecting); return moveCaretToStartOfLine (selecting);
const Rectangle<float> caretPos (getCaretRectangle().toFloat()); const Rectangle<float> caretPos (getCaretRectangle().toFloat());
return moveCaretWithTransation (indexAtPosition (caretPos.getX(), caretPos.getY() - viewport->getViewHeight()), selecting);
return moveCaretWithTransaction (indexAtPosition (caretPos.getX(), caretPos.getY() - viewport->getViewHeight()), selecting);
} }
bool TextEditor::pageDown (bool selecting) bool TextEditor::pageDown (bool selecting)
@@ -1952,14 +1909,12 @@ bool TextEditor::pageDown (bool selecting)
return moveCaretToEndOfLine (selecting); return moveCaretToEndOfLine (selecting);
const Rectangle<float> caretPos (getCaretRectangle().toFloat()); const Rectangle<float> caretPos (getCaretRectangle().toFloat());
return moveCaretWithTransation (indexAtPosition (caretPos.getX(), caretPos.getBottom() + viewport->getViewHeight()), selecting);
return moveCaretWithTransaction (indexAtPosition (caretPos.getX(), caretPos.getBottom() + viewport->getViewHeight()), selecting);
} }
void TextEditor::scrollByLines (int deltaLines) void TextEditor::scrollByLines (int deltaLines)
{ {
ScrollBar* scrollbar = viewport->getVerticalScrollBar();
if (scrollbar != nullptr)
if (ScrollBar* scrollbar = viewport->getVerticalScrollBar())
scrollbar->moveScrollbarInSteps (deltaLines); scrollbar->moveScrollbarInSteps (deltaLines);
} }
@@ -1977,24 +1932,24 @@ bool TextEditor::scrollUp()
bool TextEditor::moveCaretToTop (bool selecting) bool TextEditor::moveCaretToTop (bool selecting)
{ {
return moveCaretWithTransation (0, selecting);
return moveCaretWithTransaction (0, selecting);
} }
bool TextEditor::moveCaretToStartOfLine (bool selecting) bool TextEditor::moveCaretToStartOfLine (bool selecting)
{ {
const Rectangle<float> caretPos (getCaretRectangle().toFloat()); const Rectangle<float> caretPos (getCaretRectangle().toFloat());
return moveCaretWithTransation (indexAtPosition (0.0f, caretPos.getY()), selecting);
return moveCaretWithTransaction (indexAtPosition (0.0f, caretPos.getY()), selecting);
} }
bool TextEditor::moveCaretToEnd (bool selecting) bool TextEditor::moveCaretToEnd (bool selecting)
{ {
return moveCaretWithTransation (getTotalNumChars(), selecting);
return moveCaretWithTransaction (getTotalNumChars(), selecting);
} }
bool TextEditor::moveCaretToEndOfLine (bool selecting) bool TextEditor::moveCaretToEndOfLine (bool selecting)
{ {
const Rectangle<float> caretPos (getCaretRectangle().toFloat()); const Rectangle<float> caretPos (getCaretRectangle().toFloat());
return moveCaretWithTransation (indexAtPosition ((float) textHolder->getWidth(), caretPos.getY()), selecting);
return moveCaretWithTransaction (indexAtPosition ((float) textHolder->getWidth(), caretPos.getY()), selecting);
} }
bool TextEditor::deleteBackwards (bool moveInWholeWordSteps) bool TextEditor::deleteBackwards (bool moveInWholeWordSteps)
@@ -2128,9 +2083,9 @@ void TextEditor::focusGained (FocusChangeType)
repaint(); repaint();
updateCaretPosition(); updateCaretPosition();
ComponentPeer* const peer = getPeer();
if (peer != nullptr && ! isReadOnly())
peer->textInputRequired (getScreenPosition() - peer->getScreenPosition());
if (ComponentPeer* const peer = getPeer())
if (! isReadOnly())
peer->textInputRequired (getScreenPosition() - peer->getScreenPosition());
} }
void TextEditor::focusLost (FocusChangeType) void TextEditor::focusLost (FocusChangeType)
@@ -2142,8 +2097,7 @@ void TextEditor::focusLost (FocusChangeType)
underlinedSections.clear(); underlinedSections.clear();
ComponentPeer* const peer = getPeer();
if (peer != nullptr)
if (ComponentPeer* const peer = getPeer())
peer->dismissPendingTextInput(); peer->dismissPendingTextInput();
updateCaretPosition(); updateCaretPosition();
@@ -2160,14 +2114,10 @@ void TextEditor::resized()
updateTextHolderSize(); updateTextHolderSize();
if (! isMultiLine())
{
scrollToMakeSureCursorIsVisible();
}
else
{
if (isMultiLine())
updateCaretPosition(); updateCaretPosition();
}
else
scrollToMakeSureCursorIsVisible();
} }
void TextEditor::handleCommandMessage (const int commandId) void TextEditor::handleCommandMessage (const int commandId)
@@ -2600,8 +2550,3 @@ void TextEditor::coalesceSimilarSections()
} }
} }
} }
void TextEditor::Listener::textEditorTextChanged (TextEditor&) {}
void TextEditor::Listener::textEditorReturnKeyPressed (TextEditor&) {}
void TextEditor::Listener::textEditorEscapeKeyPressed (TextEditor&) {}
void TextEditor::Listener::textEditorFocusLost (TextEditor&) {}

+ 66
- 18
modules/juce_gui_basics/widgets/juce_TextEditor.h View File

@@ -262,16 +262,6 @@ public:
*/ */
void setSelectAllWhenFocused (bool shouldSelectAll); void setSelectAllWhenFocused (bool shouldSelectAll);
/** Sets limits on the characters that can be entered.
@param maxTextLength if this is > 0, it sets a maximum length limit; if 0, no
limit is set
@param allowedCharacters if this is non-empty, then only characters that occur in
this string are allowed to be entered into the editor.
*/
void setInputRestrictions (int maxTextLength,
const String& allowedCharacters = String::empty);
/** When the text editor is empty, it can be set to display a message. /** When the text editor is empty, it can be set to display a message.
This is handy for things like telling the user what to type in the box - the This is handy for things like telling the user what to type in the box - the
@@ -292,23 +282,23 @@ public:
@see TextEditor::addListener @see TextEditor::addListener
*/ */
class JUCE_API Listener
class Listener
{ {
public: public:
/** Destructor. */ /** Destructor. */
virtual ~Listener() {} virtual ~Listener() {}
/** Called when the user changes the text in some way. */ /** Called when the user changes the text in some way. */
virtual void textEditorTextChanged (TextEditor& editor);
virtual void textEditorTextChanged (TextEditor&) {}
/** Called when the user presses the return key. */ /** Called when the user presses the return key. */
virtual void textEditorReturnKeyPressed (TextEditor& editor);
virtual void textEditorReturnKeyPressed (TextEditor&) {}
/** Called when the user presses the escape key. */ /** Called when the user presses the escape key. */
virtual void textEditorEscapeKeyPressed (TextEditor& editor);
virtual void textEditorEscapeKeyPressed (TextEditor&) {}
/** Called when the text editor loses focus. */ /** Called when the text editor loses focus. */
virtual void textEditorFocusLost (TextEditor& editor);
virtual void textEditorFocusLost (TextEditor&) {}
}; };
/** Registers a listener to be told when things happen to the text. /** Registers a listener to be told when things happen to the text.
@@ -538,6 +528,65 @@ public:
*/ */
virtual void performPopupMenuAction (int menuItemID); virtual void performPopupMenuAction (int menuItemID);
//==============================================================================
/** Base class for input filters that can be applied to a TextEditor to restrict
the text that can be entered.
*/
class InputFilter
{
public:
InputFilter() {}
virtual ~InputFilter() {}
/** This method is called whenever text is entered into the editor.
An implementation of this class should should check the input string,
and return an edited version of it that should be used.
*/
virtual String filterNewText (TextEditor&, const String& newInput) = 0;
};
/** An input filter for a TextEditor that limits the length of text and/or the
characters that it may contain.
*/
class JUCE_API LengthAndCharacterRestriction : public InputFilter
{
public:
/** Creates a filter that limits the length of text, and/or the characters that it can contain.
@param maxTextLength if this is > 0, it sets a maximum length limit; if <= 0, no
limit is set
@param allowedCharacters if this is non-empty, then only characters that occur in
this string are allowed to be entered into the editor.
*/
LengthAndCharacterRestriction (int maxNumChars, const String& allowedCharacters);
private:
String allowedCharacters;
int maxLength;
String filterNewText (TextEditor&, const String&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LengthAndCharacterRestriction);
};
/** Sets an input filter that should be applied to this editor.
The filter can be nullptr, to remove any existing filters.
If takeOwnership is true, then the filter will be owned and deleted by the editor
when no longer needed.
*/
void setInputFilter (InputFilter* newFilter, bool takeOwnership);
/** Sets limits on the characters that can be entered.
This is just a shortcut that passes an instance of the LengthAndCharacterRestriction
class to setInputFilter().
@param maxTextLength if this is > 0, it sets a maximum length limit; if 0, no
limit is set
@param allowedCharacters if this is non-empty, then only characters that occur in
this string are allowed to be entered into the editor.
*/
void setInputRestrictions (int maxTextLength,
const String& allowedCharacters = String::empty);
//============================================================================== //==============================================================================
/** @internal */ /** @internal */
void paint (Graphics&); void paint (Graphics&);
@@ -621,7 +670,6 @@ private:
UndoManager undoManager; UndoManager undoManager;
ScopedPointer<CaretComponent> caret; ScopedPointer<CaretComponent> caret;
int maxTextLength;
Range<int> selection; Range<int> selection;
int leftIndent, topIndent; int leftIndent, topIndent;
unsigned int lastTransactionTime; unsigned int lastTransactionTime;
@@ -632,6 +680,7 @@ private:
String textToShowWhenEmpty; String textToShowWhenEmpty;
Colour colourForTextWhenEmpty; Colour colourForTextWhenEmpty;
juce_wchar passwordCharacter; juce_wchar passwordCharacter;
OptionalScopedPointer<InputFilter> inputFilter;
Value textValue; Value textValue;
enum enum
@@ -641,7 +690,6 @@ private:
draggingSelectionEnd draggingSelectionEnd
} dragType; } dragType;
String allowedCharacters;
ListenerList <Listener> listeners; ListenerList <Listener> listeners;
Array <Range<int> > underlinedSections; Array <Range<int> > underlinedSections;
@@ -661,7 +709,7 @@ private:
int indexAtPosition (float x, float y); int indexAtPosition (float x, float y);
int findWordBreakAfter (int position) const; int findWordBreakAfter (int position) const;
int findWordBreakBefore (int position) const; int findWordBreakBefore (int position) const;
bool moveCaretWithTransation (int newPos, bool selecting);
bool moveCaretWithTransaction (int newPos, bool selecting);
friend class TextHolderComponent; friend class TextHolderComponent;
friend class TextEditorViewport; friend class TextEditorViewport;
void drawContent (Graphics&); void drawContent (Graphics&);


Loading…
Cancel
Save