Request: TextEditor::getViewport()

Hi, JUCE team, would you please to add this method in TextEditor?

Viewport* getViewport() const 
{ 
    return viewport; 
}

We prefer not to expose internal implementation details like that without a really good reason…

The thing is that if we were to let users access this Viewport, then we’re committed to always using a Viewport inside a TextEditor, and would break people’s code if we ever wanted to implement it using some other kind of internal structure.

addAndMakeVisible (viewport = new TextEditorViewport (*this));

This sentence in the constructor of TextEditor, Jules :slight_smile: However using it or not, it’s always there…

I understand a little of design principles of a huge lib like JUCE and totally agree with your opinion, but if someone wants to implement drag and drop content in TextEditor (this operation will involve auto scroll the viewport), he will not be able to achieve this without the viewport…

Yeah, obviously it does use one at the moment, but my point is that as API designers, we want to leave ourselves the option of changing that in the future without it being a breaking change.

If you want to get the viewport, you can do so by poking around in the child components of the editor, and dynamic_casting them to a Viewport. But we just don’t want the class to publicly commit to that always being the case.

I added this method to TextEditor class to implement the drag and drop highlight content operation… :slight_smile:

Perhaps someone also need this function. Here is my messy code, please feel free to criticize and correct.

void MyEditor::mouseDown (const MouseEvent& e)
{
    if (getHighlightedText().isNotEmpty()
        && getHighlightedRegion().contains (getTextIndexAt (e.x, e.y))
        && !e.mods.isPopupMenu())
    {
        draggingSelected = true;
        yOfViewportWhenDragging = 0;
    }
    else
    {
        TextEditor::mouseDown (e);
    }
}

//=================================================================================================
void MyEditor::mouseDrag (const MouseEvent& e)
{
    if (draggingSelected)
    {
        setCaretVisible (false);
        setMouseCursor (e.mods.isCommandDown() ? MouseCursor::CopyingCursor
                        : MouseCursor::NormalCursor);

        float cursorX, cursorY;
        float cursorHeight = getFont().getHeight();

        getCharPosition (getTextIndexAt (e.x, e.y), cursorX, cursorY, cursorHeight);
        Rectangle<float> pos (cursorX + 8, cursorY + 10 - getViewport()->getViewPositionY(),
                              2.5f, cursorHeight);

        draggingPosition.setRectangle (pos);

        if (getViewport()->autoScroll (e.x, e.y, 50, 20))
            yOfViewportWhenDragging = getViewport()->getViewPositionY();
    }
    else
    {
        TextEditor::mouseDrag (e);
    }
}

//=====================================================================================
void MyEditor::mouseUp (const MouseEvent& e)
{
    if (e.mods.isPopupMenu())
    {
        TextEditor::mouseUp (e);
        return;
    }

    if (draggingSelected && !getHighlightedRegion().contains (getTextIndexAt (e.x, e.y)))
    {
        const String& draggingContent (getHighlightedText());
        const int dropPosition = getTextIndexAt (e.x, e.y);
        int removeNumbers = 0;        

        // clear the highlight selected and get the offset of drop position
        if (!e.mods.isCommandDown())
        {
            insertTextAtCaret (String());

            if (dropPosition > getHighlightedRegion().getEnd())
                removeNumbers = draggingContent.length();
        }

        if (yOfViewportWhenDragging > 0)
            getViewport()->setViewPosition (0, yOfViewportWhenDragging);

        setCaretPosition (dropPosition - removeNumbers);
        insertTextAtCaret (draggingContent);
    }
    else
    {
        TextEditor::mouseUp (e);
        const int caretPos = getCaretPosition();

        if (caretPos != getHighlightedRegion().getStart()
            && getHighlightedRegion().contains (getTextIndexAt (e.x, e.y)))
            setCaretPosition (getCaretPosition());
    }

    setMouseCursor (MouseCursor::IBeamCursor);
    setCaretVisible (true);

    draggingSelected = false;
    yOfViewportWhenDragging = 0;
    draggingPosition.setRectangle (Rectangle<float> (0, 0, 0, 0));
}

//=====================================================================================
void MyEditor::mouseMove (const MouseEvent& e)
{
    if (getHighlightedText().isNotEmpty()
        && getHighlightedRegion().contains (getTextIndexAt (e.x - 5, e.y)))
        setMouseCursor (MouseCursor::NormalCursor);
    else
        setMouseCursor (MouseCursor::IBeamCursor);
}

//=====================================================================================
void MyEditor::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& d)
{
    TextEditor::mouseWheelMove (e, d);

    if (draggingSelected && getHighlightedText().isNotEmpty())
        yOfViewportWhenDragging = getViewport()->getViewPositionY();
}