mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
iOS: Add proper UITextInput implementation
This provides an improved user experience, allowing cursor movement directly from the keyboard.
This commit is contained in:
parent
09c107698b
commit
5cf1a964fc
8 changed files with 1022 additions and 255 deletions
|
|
@ -71,21 +71,7 @@ private:
|
|||
|
||||
void setSelection (Range<int> r) override
|
||||
{
|
||||
if (r == codeEditorComponent.getHighlightedRegion())
|
||||
return;
|
||||
|
||||
if (r.isEmpty())
|
||||
{
|
||||
codeEditorComponent.caretPos.setPosition (r.getStart());
|
||||
return;
|
||||
}
|
||||
|
||||
auto& doc = codeEditorComponent.document;
|
||||
|
||||
const auto cursorAtStart = r.getEnd() == codeEditorComponent.getHighlightedRegion().getStart()
|
||||
|| r.getEnd() == codeEditorComponent.getHighlightedRegion().getEnd();
|
||||
codeEditorComponent.selectRegion (CodeDocument::Position (doc, cursorAtStart ? r.getEnd() : r.getStart()),
|
||||
CodeDocument::Position (doc, cursorAtStart ? r.getStart() : r.getEnd()));
|
||||
codeEditorComponent.setHighlightedRegion (r);
|
||||
}
|
||||
|
||||
String getText (Range<int> r) const override
|
||||
|
|
@ -108,32 +94,7 @@ private:
|
|||
|
||||
RectangleList<int> getTextBounds (Range<int> textRange) const override
|
||||
{
|
||||
auto& doc = codeEditorComponent.document;
|
||||
|
||||
RectangleList<int> localRects;
|
||||
|
||||
CodeDocument::Position startPosition (doc, textRange.getStart());
|
||||
CodeDocument::Position endPosition (doc, textRange.getEnd());
|
||||
|
||||
for (int line = startPosition.getLineNumber(); line <= endPosition.getLineNumber(); ++line)
|
||||
{
|
||||
CodeDocument::Position lineStart (doc, line, 0);
|
||||
CodeDocument::Position lineEnd (doc, line, doc.getLine (line).length());
|
||||
|
||||
if (line == startPosition.getLineNumber())
|
||||
lineStart = lineStart.movedBy (startPosition.getIndexInLine());
|
||||
|
||||
if (line == endPosition.getLineNumber())
|
||||
lineEnd = { doc, line, endPosition.getIndexInLine() };
|
||||
|
||||
auto startPos = codeEditorComponent.getCharacterBounds (lineStart).getTopLeft();
|
||||
auto endPos = codeEditorComponent.getCharacterBounds (lineEnd).getTopLeft();
|
||||
|
||||
localRects.add (startPos.x,
|
||||
startPos.y,
|
||||
jmax (1, endPos.x - startPos.x),
|
||||
codeEditorComponent.getLineHeight());
|
||||
}
|
||||
const auto localRects = codeEditorComponent.getTextBounds (textRange);
|
||||
|
||||
RectangleList<int> globalRects;
|
||||
|
||||
|
|
@ -213,11 +174,23 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
void getHighlightArea (RectangleList<float>& area, float x, int y, int lineH, float characterWidth) const
|
||||
Optional<Rectangle<float>> getHighlightArea (float x, int y, int lineH, float characterWidth) const
|
||||
{
|
||||
if (highlightColumnStart < highlightColumnEnd)
|
||||
area.add (Rectangle<float> (x + (float) highlightColumnStart * characterWidth - 1.0f, (float) y - 0.5f,
|
||||
(float) (highlightColumnEnd - highlightColumnStart) * characterWidth + 1.5f, (float) lineH + 1.0f));
|
||||
return getHighlightArea (x, y, lineH, characterWidth, { highlightColumnStart, highlightColumnEnd });
|
||||
}
|
||||
|
||||
Optional<Rectangle<float>> getHighlightArea (float x,
|
||||
int y,
|
||||
int lineH,
|
||||
float characterWidth,
|
||||
Range<int> highlightColumns) const
|
||||
{
|
||||
if (highlightColumns.isEmpty())
|
||||
return {};
|
||||
|
||||
return Rectangle<float> (x + (float) highlightColumns.getStart() * characterWidth - 1.0f, (float) y - 0.5f,
|
||||
(float) (highlightColumns.getEnd() - highlightColumns.getStart()) * characterWidth + 1.5f, (float) lineH + 1.0f);
|
||||
|
||||
}
|
||||
|
||||
void draw (CodeEditorComponent& owner, Graphics& g, const Font& fontToUse,
|
||||
|
|
@ -533,14 +506,6 @@ void CodeEditorComponent::setTemporaryUnderlining (const Array<Range<int>>&)
|
|||
jassertfalse; // TODO Windows IME not yet supported for this comp..
|
||||
}
|
||||
|
||||
Rectangle<int> CodeEditorComponent::getCaretRectangle()
|
||||
{
|
||||
if (caret != nullptr)
|
||||
return getLocalArea (caret.get(), caret->getLocalBounds());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void CodeEditorComponent::setLineNumbersShown (const bool shouldBeShown)
|
||||
{
|
||||
if (showLineNumbers != shouldBeShown)
|
||||
|
|
@ -598,25 +563,26 @@ void CodeEditorComponent::paint (Graphics& g)
|
|||
{
|
||||
g.fillAll (findColour (CodeEditorComponent::backgroundColourId));
|
||||
|
||||
auto gutterSize = getGutterSize();
|
||||
auto bottom = horizontalScrollBar.isVisible() ? horizontalScrollBar.getY() : getHeight();
|
||||
auto right = verticalScrollBar.isVisible() ? verticalScrollBar.getX() : getWidth();
|
||||
const auto gutterSize = getGutterSize();
|
||||
const auto bottom = horizontalScrollBar.isVisible() ? horizontalScrollBar.getY() : getHeight();
|
||||
const auto right = verticalScrollBar.isVisible() ? verticalScrollBar.getX() : getWidth();
|
||||
|
||||
g.reduceClipRegion (gutterSize, 0, right - gutterSize, bottom);
|
||||
|
||||
g.setFont (font);
|
||||
|
||||
auto clip = g.getClipBounds();
|
||||
auto firstLineToDraw = jmax (0, clip.getY() / lineHeight);
|
||||
auto lastLineToDraw = jmin (lines.size(), clip.getBottom() / lineHeight + 1);
|
||||
auto x = (float) (gutterSize - xOffset * charWidth);
|
||||
auto rightClip = (float) clip.getRight();
|
||||
const auto clip = g.getClipBounds();
|
||||
const auto firstLineToDraw = jmax (0, clip.getY() / lineHeight);
|
||||
const auto lastLineToDraw = jmin (lines.size(), clip.getBottom() / lineHeight + 1);
|
||||
const auto x = (float) (gutterSize - xOffset * charWidth);
|
||||
const auto rightClip = (float) clip.getRight();
|
||||
|
||||
{
|
||||
RectangleList<float> highlightArea;
|
||||
|
||||
for (int i = firstLineToDraw; i < lastLineToDraw; ++i)
|
||||
lines.getUnchecked(i)->getHighlightArea (highlightArea, x, lineHeight * i, lineHeight, charWidth);
|
||||
if (const auto area = lines.getUnchecked(i)->getHighlightArea (x, lineHeight * i, lineHeight, charWidth))
|
||||
highlightArea.add (*area);
|
||||
|
||||
g.setColour (findColour (CodeEditorComponent::highlightColourId));
|
||||
g.fillRectList (highlightArea);
|
||||
|
|
@ -893,6 +859,37 @@ CodeDocument::Position CodeEditorComponent::getPositionAt (int x, int y) const
|
|||
return CodeDocument::Position (document, line, index);
|
||||
}
|
||||
|
||||
int CodeEditorComponent::getCharIndexForPoint (Point<int> point) const
|
||||
{
|
||||
return getPositionAt (point.x, point.y).getPosition();
|
||||
}
|
||||
|
||||
RectangleList<int> CodeEditorComponent::getTextBounds (Range<int> textRange) const
|
||||
{
|
||||
RectangleList<int> localRects;
|
||||
|
||||
const CodeDocument::Position startPosition (document, textRange.getStart());
|
||||
const CodeDocument::Position endPosition (document, textRange.getEnd());
|
||||
|
||||
for (int line = startPosition.getLineNumber(); line <= endPosition.getLineNumber(); ++line)
|
||||
{
|
||||
const CodeDocument::Position lineStartColumn0 { document, line, 0 };
|
||||
|
||||
const auto lineStart = line == startPosition.getLineNumber() ? lineStartColumn0.movedBy (startPosition.getIndexInLine())
|
||||
: lineStartColumn0;
|
||||
|
||||
const CodeDocument::Position lineEnd { document, line, line == endPosition.getLineNumber() ? endPosition.getIndexInLine()
|
||||
: document.getLine (line).length() };
|
||||
|
||||
const auto startPos = getCharacterBounds (lineStart).getTopLeft();
|
||||
const auto endPos = getCharacterBounds (lineEnd) .getTopLeft();
|
||||
|
||||
localRects.add (startPos.x, startPos.y, jmax (1, endPos.x - startPos.x), getLineHeight());
|
||||
}
|
||||
|
||||
return localRects;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CodeEditorComponent::insertTextAtCaret (const String& newText)
|
||||
{
|
||||
|
|
@ -1336,8 +1333,13 @@ bool CodeEditorComponent::isHighlightActive() const noexcept
|
|||
|
||||
void CodeEditorComponent::setHighlightedRegion (const Range<int>& newRange)
|
||||
{
|
||||
selectRegion (CodeDocument::Position (document, newRange.getStart()),
|
||||
CodeDocument::Position (document, newRange.getEnd()));
|
||||
if (newRange == getHighlightedRegion())
|
||||
return;
|
||||
|
||||
const auto cursorAtStart = newRange.getEnd() == getHighlightedRegion().getStart()
|
||||
|| newRange.getEnd() == getHighlightedRegion().getEnd();
|
||||
selectRegion (CodeDocument::Position (document, cursorAtStart ? newRange.getEnd() : newRange.getStart()),
|
||||
CodeDocument::Position (document, cursorAtStart ? newRange.getStart() : newRange.getEnd()));
|
||||
}
|
||||
|
||||
String CodeEditorComponent::getTextInRange (const Range<int>& range) const
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ public:
|
|||
/** Returns the current caret position. */
|
||||
CodeDocument::Position getCaretPos() const { return caretPos; }
|
||||
|
||||
/** Returns the position of the caret, relative to the editor's origin. */
|
||||
Rectangle<int> getCaretRectangle() override;
|
||||
/** Returns the total number of codepoints in the string. */
|
||||
int getTotalNumChars() const override { return document.getNumCharacters(); }
|
||||
|
||||
/** Moves the caret.
|
||||
If selecting is true, the section of the document between the current
|
||||
|
|
@ -121,6 +121,26 @@ public:
|
|||
/** Enables or disables the line-number display in the gutter. */
|
||||
void setLineNumbersShown (bool shouldBeShown);
|
||||
|
||||
/** Returns the number of characters from the beginning of the document to the caret. */
|
||||
int getCaretPosition() const override { return getCaretPos().getPosition(); }
|
||||
|
||||
/** @see getPositionAt */
|
||||
int getCharIndexForPoint (Point<int> point) const override;
|
||||
|
||||
/** Returns the bounds of the caret at a particular location in the text. */
|
||||
Rectangle<int> getCaretRectangleForCharIndex (int index) const override
|
||||
{
|
||||
return getCharacterBounds ({ document, index });
|
||||
}
|
||||
|
||||
/** Returns the bounding box for a range of text in the editor. As the range may span
|
||||
multiple lines, this method returns a RectangleList.
|
||||
|
||||
The bounds are relative to the component's top-left and may extend beyond the bounds
|
||||
of the component if the text is long and word wrapping is disabled.
|
||||
*/
|
||||
RectangleList<int> getTextBounds (Range<int> textRange) const override;
|
||||
|
||||
//==============================================================================
|
||||
bool moveCaretLeft (bool moveInWholeWordSteps, bool selecting);
|
||||
bool moveCaretRight (bool moveInWholeWordSteps, bool selecting);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue