Qml TextArea的行号/行高

时间:2019-07-04 16:31:09

标签: qt qml qtquick2 kde

我们想在基于QtQuick的应用程序中实现嵌入式代码编辑器。为了突出显示,我们使用基于QSyntaxHighlighter的{​​{1}}。我们找不到确定行高和行距的方法,该方法无法使我们在代码旁边显示行号。支持动态换行也将是一个很好的补充。

KSyntaxHighlighting

如您所见,我们使用 Flickable { id: flickable flickableDirection: Flickable.VerticalFlick Layout.preferredWidth: parent.width Layout.maximumWidth: parent.width Layout.minimumHeight: 200 Layout.fillHeight: true Layout.fillWidth: true boundsBehavior: Flickable.StopAtBounds clip: true ScrollBar.vertical: ScrollBar { width: 15 active: true policy: ScrollBar.AlwaysOn } property int rowHeight: textArea.font.pixelSize+3 property int marginsTop: 10 property int marginsLeft: 4 property int lineCountWidth: 40 Column { id: lineNumbers anchors.left: parent.left anchors.leftMargin: flickable.marginsLeft anchors.topMargin: flickable.marginsTop y: flickable.marginsTop width: flickable.lineCountWidth function range(start, end) { var rangeArray = new Array(end-start); for(var i = 0; i < rangeArray.length; i++){ rangeArray[i] = start+i; } return rangeArray; } Repeater { model: textArea.lineCount delegate: Label { color: (!visualization.urdfPreviewIsOK && (index+1) === visualization.urdfPreviewErrorLine) ? "white" : "#666" font: textArea.font width: parent.width horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter height: flickable.rowHeight renderType: Text.NativeRendering text: index+1 background: Rectangle { color: (!visualization.urdfPreviewIsOK && (index+1) === visualization.urdfPreviewErrorLine) ? "red" : "white" } } } } Rectangle { y: 4 height: parent.height anchors.left: parent.left anchors.leftMargin: flickable.lineCountWidth + flickable.marginsLeft width: 1 color: "#ddd" } TextArea.flickable: TextArea { id: textArea property bool differentFromSavedState: fileManager.textDifferentFromSaved text: fileManager.textTmpState textFormat: Qt.PlainText //dont wrap to allow for easy line annotation wrapMode: TextArea.Wrap focus: false selectByMouse: true leftPadding: flickable.marginsLeft+flickable.lineCountWidth rightPadding: flickable.marginsLeft topPadding: flickable.marginsTop bottomPadding: flickable.marginsTop background: Rectangle { color: "white" border.color: "green" border.width: 1.5 } Component.onCompleted: { fileManager.textEdit = textArea.textDocument } onTextChanged: { fileManager.textTmpState = text } function update() { text = fileManager.textTmpState } } } 来猜测行高和行距,但是,当然,只要DPI或系统的其他属性发生变化,行高和行距就会中断。

3 个答案:

答案 0 :(得分:1)

TextArea类型具有两个属性contentWidthcontentHeight,其中包含文本内容的大小。

因此,如果将高度除以行数(可以通过属性lineCount获得),则将获得行的高度:

property int rowHeight: textArea.contentHeight / textArea.lineCount

但是,如果您打算在同一文档中使用多个行距,则必须通过操作QTextDocument处理每一行:

class LineManager: public QObject
{
    Q_OBJECT
    Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged)
public:
    LineManager(): QObject(), document(nullptr)
    {}
    Q_INVOKABLE void setDocument(QQuickTextDocument* qdoc)
    {
        document = qdoc->textDocument();
        connect(document, &QTextDocument::blockCountChanged, this, &LineManager::lineCountChanged);
    }

    Q_INVOKABLE int lineCount() const
    {
        if (!document)
            return 0;
        return document->blockCount();
    }

    Q_INVOKABLE int height(int lineNumber) const
    {
        return int(document->documentLayout()->blockBoundingRect(document->findBlockByNumber(lineNumber)).height());
    }
signals:
    void lineCountChanged();
private:
    QTextDocument* document;
};
    LineManager* mgr = new LineManager();
    QQuickView *view = new QQuickView;
    view->rootContext()->setContextProperty("lineCounter", mgr);
    view->setSource(QUrl("qrc:/main.qml"));
    view->show();
Repeater {
    model: lineCounter.lineCount
    delegate:
        Label {
            color: "#666"
            font: textArea.font
            width: parent.width
            height: lineCounter.height(index)
            horizontalAlignment: Text.AlignRight
            verticalAlignment: Text.AlignVCenter
            renderType: Text.NativeRendering
            text: index+1
            background: Rectangle {
                border.color: "black"
            }
    }
}

答案 1 :(得分:0)

我找到了仅QML解决方案:

  1. 使用TextEdit而不是TextArea来避免行号和文本之间的对齐问题
  2. 使用“ ListView”为文本编辑生成行号:

这是一个初步的解决方案:

    RowLayout {
        anchors.fill: parent
        ListView {
            Layout.preferredWidth: 30
            Layout.fillHeight: true
            model: textEdit.text.split(/\n/g)
            delegate: Text { text: index + 1 }
        }
        TextEdit {
            id: textEdit
            Layout.fillWidth: true
            Layout.fillHeight: true
        }
    }

ListView具有每行文本的完整副本。我们可以使用此副本来计算行高(考虑自动换行)。为此,我们创建了一个不可见的Text。我们可以通过在Flickable上添加TextEdit并同步ListViewTextEdit之间的滚动来进一步改善答案:

这是一个更完整的解决方案:

// NumberedTextEdit.qml

import QtQuick 2.12
import QtQuick.Controls 2.5

Item {
    property alias lineNumberFont: lineNumbers.textMetrics.font
    property color lineNumberBackground: "#e0e0e0"
    property color lineNumberColor: "black"
    property alias font: textEdit.font
    property alias text: textEdit.text
    property color textBackground: "white"
    property color textColor: "black"

    Rectangle {
        anchors.fill: parent

        color: textBackground

        ListView {
            id: lineNumbers
            property TextMetrics textMetrics: TextMetrics { text: "99999"; font: textEdit.font }
            model: textEdit.text.split(/\n/g)
            anchors.left: parent.left
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.margins: 10
            width: textMetrics.boundingRect.width
            clip: true

            delegate: Rectangle {
                width: lineNumbers.width
                height: lineText.height
                color: lineNumberBackground
                Text {
                    id: lineNumber
                    anchors.horizontalCenter: parent.horizontalCenter
                    text: index + 1
                    color: lineNumberColor
                    font: textMetrics.font
                }

                Text {
                    id: lineText
                    width: flickable.width
                    text: modelData
                    font: textEdit.font
                    visible: false
                    wrapMode: Text.WordWrap
                }
            }
            onContentYChanged: {
                if (!moving) return
                flickable.contentY = contentY
            }
        }

        Item {
            anchors.left: lineNumbers.right
            anchors.right: parent.right
            anchors.top: parent.top
            anchors.bottom: parent.bottom
            anchors.margins: 10

            Flickable {
                id: flickable
                anchors.fill: parent
                clip: true
                contentWidth: textEdit.width
                contentHeight: textEdit.height
                TextEdit {
                    id: textEdit
                    width: flickable.width
                    color: textColor
                    wrapMode: Text.WordWrap
                }
                onContentYChanged: {
                    if (lineNumbers.moving) return
                    lineNumbers.contentY = contentY
                }
            }
        }
    }
}

答案 2 :(得分:0)

我发现您可以使用 FontMetrics 查询行高,然后通过 Math.ceil(fontMetrics.lineSpacing) 获取真实高度,例如:

TextEdit {
    id: textArea

     FontMetrics {
         id: fontMetricsId
         font: textArea.font
     }

     Component.onCompleted: {
          console.log("Line spacing:" + Math.ceil(fontMetricsId.lineSpacing)
     }
}