Search code examples
qtqmlqtquick2qtquickcontrols2

How to make a TextArea have a max size and a scroll bar?


I have a TextArea that will usually only contain about one line of text. I do, however, want the user to be able to add more than that and have the TextArea expand up to a maximum of about 15 lines, at which point any additional text can be accessed via a scroll bar. I've been able to get the scrolling aspect working by having the TextArea be contained in a Flickable (which is ultimately contained in a Rectangle).

Rectangle {
    id: rec
    width: 200
    height: 25

    Flickable {
        id: flickable
        anchors.fill: parent
        contentWidth: textArea.width
        contentHeight: textArea.height

        TextArea.flickable:
            TextArea {
            id: textArea
            text: qsTr("Hello, world!")
            wrapMode: Text.WordWrap
        }
        ScrollBar.vertical: ScrollBar { }
    }
}

At this point, how would I go about having the text box expand with the text until some pre-defined max number of pixels (say, 300)?

Edit

Alright, almost there, just having one problem with getting the text to center properly with Mitch's solution. My main.qml file contains the following:

import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.3

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    HelloWorld{
        anchors.centerIn: parent
    }
}

Any my HelloWorld.qml file contains the following:

import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.4

ColumnLayout{

    width: 250

    FontMetrics {
        id: fontMetrics
        font: textArea.font
    }

    Flickable {
        id: flickable
        width: parent.width
        height: Math.min(contentHeight, fontMetrics.height * 15)
        contentWidth: width
        contentHeight: textArea.implicitHeight
        clip: true

        TextArea.flickable: TextArea {
            id: textArea
            text: "Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! "
                + "Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! "
            wrapMode: Text.WordWrap
            //padding: 0

            background: Rectangle {
                border.color: "blue"
            }
        }
        ScrollBar.vertical: ScrollBar {}
    }

}

This is very close to working, but for one reason or another, when I have this code outside of main.qml, the text gets shifted lower and to the right until the user selects it:

enter image description here

After selecting the text, it looks like this (which is what I want it to start at): enter image description here


Solution

  • Set the height of the Flickable to be contentHeight or 300 - whichever is smaller:

    import QtQuick 2.12
    import QtQuick.Controls 2.12
    
    ApplicationWindow {
        width: 400
        height: 400
        color: "#444"
        visible: true
    
        Rectangle {
            anchors.fill: flickable
        }
    
        Flickable {
            id: flickable
            width: parent.width
            height: Math.min(contentHeight, 300)
            contentWidth: width
            contentHeight: textArea.implicitHeight
    
            TextArea.flickable: TextArea {
                id: textArea
                text: qsTr("Hello, world! Hello, world! Hello, world! Hello, world! ")
                wrapMode: Text.WordWrap
            }
            ScrollBar.vertical: ScrollBar {}
        }
    }
    

    textarea

    If you don't want the Rectangle to be where it is (a sibling of Flickable), you can remove it, add

    clip: true
    

    to the Flickable and

    background: Rectangle {}
    

    to the TextArea.

    Also, if you want to be a bit more precise, you can use FontMetrics to calculate the line height:

    import QtQuick 2.12
    import QtQuick.Controls 2.12
    
    ApplicationWindow {
        width: 400
        height: 400
        color: "#444"
        visible: true
    
        FontMetrics {
            id: fontMetrics
            font: textArea.font
        }
    
        Flickable {
            id: flickable
            width: parent.width
            height: Math.min(contentHeight, fontMetrics.height * 15)
            contentWidth: width
            contentHeight: textArea.implicitHeight
            clip: true
    
            TextArea.flickable: TextArea {
                id: textArea
                text: "Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! "
                    + "Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! Hello, world! "
                wrapMode: Text.WordWrap
                padding: 0
    
                background: Rectangle {}
            }
            ScrollBar.vertical: ScrollBar {}
        }
    }