QML TreeView 的可重用编辑器委托

Reuseable EditorDelegate for QML TreeView

我正在尝试弄清楚如何为 QML TreeView 组件创建可重用的 EditorDelegate

我能够为角色 end 的一个专栏创建一个可行的编辑委托。但是我的TreeView有3列,分别是namestartend

我试图简单地设置 styleData.value=textEdit.text 而不是 modelEnd=textEdit.text,但看起来 styleData.value 是只读的 属性。

如何使我的 EditorDelegate 可重复用于所有列?

EditorDelegate.qml 导入 QtQuick 2.0

Rectangle {
    anchors.fill: parent

    Text {
        anchors.fill: parent
        id: textDisplay
        visible: true
        text: styleData.value
    }

    TextInput {
        anchors.fill: parent
        id: textEdit
        text: styleData.value
        visible: false
        onVisibleChanged: {
            focus: parent
        }

        onAccepted: {
            model.end=textEdit.text; // Can be model.name, model.start, model.<role>???
            textEdit.visible=false
            textDisplay.visible=true
        }
        onFocusChanged: {
            if (!focus) {
                textEdit.visible=false
                styleData.value=textEdit.text
                textDisplay.visible=true
            }

        }
    }

    MouseArea {
        id: mouseArea
        acceptedButtons: Qt.AllButtons

        anchors.fill: parent

        onDoubleClicked: {
            if (mouse.button & Qt.LeftButton) {
                textDisplay.visible=false
                textEdit.visible=true
                textEdit.forceActiveFocus()
            }
        }
    }
}

用法应该是这样的:

import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 1.4

Window {
    visible: true

    TreeView {
        id: treeView
        anchors.fill: parent

        TableViewColumn {
            title: "Value"
            role: "name"
            delegate: EditorDelegate { }
        }        

        TableViewColumn {
            title: "Start"
            id: start
            role: "start"
            delegate: EditorDelegate {   }
        }

        TableViewColumn {
            title: "End"
            id: end
            role: "end"
            delegate: EditorDelegate {  }
        }

        model: itemModel
    }
}

这里我还有一个问题,因为EditorDelegate 无法打开和折叠树节点。但这是一个完全不同的故事。

这个想法是一个组件清楚地建立了入口和出口,在你的情况下,组件与视图非常相关,所以它很难重用。更好的设计是只暴露一个属性,它用模型的数据更新,并在它发生变化时通知它。

在委托中,我更喜欢在必要时使用 Loader 来显示编辑器。

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStandardItemModel>

#include <QDebug>

enum CustomRoles {
    NameRole = Qt::UserRole + 1000,
    StartRole,
    EndRole
};

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QStandardItemModel model;
    QObject::connect(&model, &QStandardItemModel::itemChanged, [](QStandardItem *item){
        qDebug()<< item->data(StartRole);
    });
    QHash<int, QByteArray> roles;
    roles[NameRole] = "name";
    roles[StartRole] = "start";
    roles[EndRole] = "end";
    model.setItemRoleNames(roles);

    for(int i=0; i<4; ++i){
        QStandardItem *parent_item = new QStandardItem();
        model.invisibleRootItem()->appendRow(parent_item);
        parent_item->setData(QString("name-%1").arg(i), NameRole);
        parent_item->setData(QString("start-%1").arg(i), StartRole);
        parent_item->setData(QString("end-%1").arg(i), EndRole);
        for (int j=0; j<5; ++j) {
            QStandardItem *child_item = new QStandardItem();
            parent_item->appendRow(child_item);
            child_item->setData(QString("name-%1-%2").arg(i).arg(j), NameRole);
            child_item->setData(QString("start-%1-%2").arg(i).arg(j), StartRole);
            child_item->setData(QString("end-%1-%2").arg(i).arg(j), EndRole);
        }
    }
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("itemModel", &model);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
    TreeView {
        id: treeView
        anchors.fill: parent
        TableViewColumn {
            title: "Value"
            role: "name"
            delegate: EditorDelegate{
                text: styleData.value
                onTextChanged: model.name = text
            }
        }
        TableViewColumn {
            title: "Start"
            id: start
            role: "start"
            delegate: EditorDelegate{
                text: styleData.value
                onTextChanged: model.start = text
            }
        }
        TableViewColumn {
            title: "End"
            id: end
            role: "end"
            delegate: EditorDelegate{
                text: styleData.value
                onTextChanged: model.end = text
            }
        }
        model: itemModel
    }
}

EditorDelegate.qml

import QtQuick 2.0

Rectangle {
    id: root
    property string text
    property bool mode: false
    Component{
        id: component_display
        Text{}
    }
    Component{
        id: component_edit
        TextInput{}
    }
    Loader{
        id: loader
        anchors.fill: parent
        sourceComponent: mode ? component_edit: component_display
        onSourceComponentChanged: {
            loader.item.text = root.text
            if(sourceComponent === component_edit){
                loader.item.editingFinished.connect(onEditingFinished)
                loader.item.forceActiveFocus()
            }
        }
        function onEditingFinished(){
            text = loader.item.text
            mode = false
        }
        MouseArea{
            anchors.fill: parent
            onDoubleClicked: {
                if (mouse.button & Qt.LeftButton)
                    mode = true
            }
        }
    }
}