From 1092518fe7543cd9fcf57bc4d7806acd6fa673b2 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 6 Mar 2015 11:45:57 +0100 Subject: [PATCH] - logs pane below the status pane - retrieve javascript message --- mix/AppContext.cpp | 4 + mix/CodeEditorExtensionManager.cpp | 6 +- mix/qml/LogsPane.qml | 143 ++++++++++++++++++++++++ mix/qml/MainContent.qml | 14 +-- mix/qml/StatusPane.qml | 93 +++++++++++++--- mix/qml/WebPreview.qml | 11 +- mix/qml/img/broom.png | Bin 0 -> 501 bytes mix/qml/img/copy.png | Bin 0 -> 342 bytes mix/res.qrc | 4 + mix/sortfilterproxymodel.cpp | 170 +++++++++++++++++++++++++++++ mix/sortfilterproxymodel.h | 107 ++++++++++++++++++ 11 files changed, 522 insertions(+), 30 deletions(-) create mode 100644 mix/qml/LogsPane.qml create mode 100644 mix/qml/img/broom.png create mode 100644 mix/qml/img/copy.png create mode 100644 mix/sortfilterproxymodel.cpp create mode 100644 mix/sortfilterproxymodel.h diff --git a/mix/AppContext.cpp b/mix/AppContext.cpp index 29124a39a..46bb0f98d 100644 --- a/mix/AppContext.cpp +++ b/mix/AppContext.cpp @@ -22,6 +22,7 @@ * - KeyEventManager */ +#include #include #include #include @@ -37,6 +38,7 @@ #include "QVariableDefinition.h" #include "HttpServer.h" #include "AppContext.h" +#include "sortfilterproxymodel.h" using namespace dev; using namespace dev::eth; @@ -74,6 +76,7 @@ void AppContext::load() qmlRegisterType("org.ethereum.qml.QBoolType", 1, 0, "QBoolType"); qmlRegisterType("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration"); qmlRegisterType("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry"); + qmlRegisterType("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel"); QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml")); QObject* projectModel = projectModelComponent.create(); if (projectModelComponent.isError()) @@ -86,6 +89,7 @@ void AppContext::load() m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel); qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType("HttpServer", 1, 0, "HttpServer"); + m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); QWindow *window = qobject_cast(m_applicationEngine->rootObjects().at(0)); window->setIcon(QIcon(":/res/mix_256x256x32.png")); diff --git a/mix/CodeEditorExtensionManager.cpp b/mix/CodeEditorExtensionManager.cpp index 97b808eb2..516184cff 100644 --- a/mix/CodeEditorExtensionManager.cpp +++ b/mix/CodeEditorExtensionManager.cpp @@ -53,10 +53,10 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor) void CodeEditorExtensionManager::initExtensions() { - std::shared_ptr output = std::make_shared(m_appContext); - QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); + //std::shared_ptr output = std::make_shared(m_appContext); + //QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight); - initExtension(output); + //initExtension(output); } void CodeEditorExtensionManager::initExtension(std::shared_ptr _ext) diff --git a/mix/qml/LogsPane.qml b/mix/qml/LogsPane.qml new file mode 100644 index 000000000..63d940eb6 --- /dev/null +++ b/mix/qml/LogsPane.qml @@ -0,0 +1,143 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.1 +import org.ethereum.qml.SortFilterProxyModel 1.0 +import "." + +Rectangle +{ + function push(_level, _type, _content) + { + _content = _content.replace(/\n/g, " ") + logsModel.insert(0, { "type": _type, "date": Qt.formatDateTime(new Date(), "hh:mm:ss dd.MM.yyyy"), "content": _content, "level": _level }); + } + anchors.fill: parent + radius: 5 + color: "#f7f7f7" + border.color: "#808080" + border.width: 2 + ColumnLayout { + height: parent.height - 4 + width: parent.width - 2 + spacing: 0 + Row + { + id: rowAction + Layout.preferredHeight: 35 + height: 35 + anchors.leftMargin: 10 + anchors.left: parent.left + spacing: 5 + Button + { + height: 30 + anchors.verticalCenter: parent.verticalCenter + action: clearAction + iconSource: "qrc:/qml/img/broom.png" + } + + Action { + id: clearAction + enabled: logsModel.count > 0 + tooltip: qsTr("Clear") + onTriggered: { + logsModel.clear() + } + } + + Button + { + height: 30 + anchors.verticalCenter: parent.verticalCenter + action: copytoClipBoardAction + iconSource: "qrc:/qml/img/copy.png" + } + + Action { + id: copytoClipBoardAction + enabled: logsModel.count > 0 + tooltip: qsTr("Copy to Clipboard") + onTriggered: { + var content = ""; + for (var k = 0; k < logsModel.count; k++) + { + var log = logsModel.get(k); + content += log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content; + } + appContext.toClipboard(content); + } + } + } + + ListModel { + id: logsModel + } + + TableView { + id: logsTable + clip: true + Layout.fillWidth: true + Layout.preferredHeight: parent.height - rowAction.height + headerVisible : false + onDoubleClicked: + { + var log = logsModel.get((logsTable.currentRow)); + appContext.toClipboard(log.type + " " + log.level + " " + log.date + " " + log.content); + } + + model: SortFilterProxyModel { + id: proxyModel + source: logsModel + + filterRole: "" + filterString: "" + filterSyntax: SortFilterProxyModel.Wildcard + filterCaseSensitivity: Qt.CaseInsensitive + } + TableViewColumn + { + role: "date" + title: qsTr("date") + width: 150 + delegate: itemDelegate + } + TableViewColumn + { + role: "type" + title: qsTr("type") + width: 100 + delegate: itemDelegate + } + TableViewColumn + { + role: "content" + title: qsTr("content") + width: 700 + delegate: itemDelegate + } + } + + Component { + id: itemDelegate + DefaultLabel { + text: styleData.value; + font.family: "sans serif" + font.pointSize: Style.absoluteSize(-1) + color: { + if (styleData.row > -1) + { + var l = logsModel.get(styleData.row).level + if (l === "error") + return "red" + else if (l === "warning") + return "orange" + else if (l === "info") + return "#808080" + } + else + return "#808080" + } + } + } + } +} diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index 081e0cd95..d295e9da0 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -102,7 +102,7 @@ Rectangle { } CodeEditorExtensionManager { - headerView: headerPaneTabs; + //headerView: headerPaneTabs; } Settings { @@ -133,16 +133,10 @@ Rectangle { } id: headerPaneContainer anchors.fill: parent - TabView { - id: headerPaneTabs - tabsVisible: false - antialiasing: true + StatusPane + { anchors.fill: parent - style: TabViewStyle { - frameOverlap: 1 - tab: Rectangle {} - frame: Rectangle { color: "transparent" } - } + webPreview: webPreview } } } diff --git a/mix/qml/StatusPane.qml b/mix/qml/StatusPane.qml index e526d65bd..87bfb39d5 100644 --- a/mix/qml/StatusPane.qml +++ b/mix/qml/StatusPane.qml @@ -8,6 +8,7 @@ import "." Rectangle { id: statusHeader objectName: "statusPane" + property variant webPreview function updateStatus(message) { @@ -15,7 +16,6 @@ Rectangle { { status.state = ""; status.text = qsTr("Compile successfully."); - logslink.visible = false; debugImg.state = "active"; } else @@ -23,39 +23,52 @@ Rectangle { status.state = "error"; var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true); status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; - logslink.visible = true; debugImg.state = ""; } debugRunActionIcon.enabled = codeModel.hasContract; } - function infoMessage(text) + function infoMessage(text, type) { status.state = ""; status.text = text - logslink.visible = false; + logPane.push("info",type, text); } - function errorMessage(text) + function warningMessage(text, type) + { + status.state = ""; + status.text = text + logPane.push("warning", type, text); + } + + function errorMessage(text, type) { status.state = "error"; status.text = text - logslink.visible = false; + logPane.push("error", type, text); + } + + Connections { + target: webPreview + onJavaScriptErrorMessage: errorMessage(_content, "javascript") + onJavaScriptWarningMessage: warningMessage(_content, "javascript") + onJavaScriptInfoMessage: infoMessage(_content, "javascript") } Connections { target:clientModel - onRunStarted: infoMessage(qsTr("Running transactions...")); - onRunFailed: errorMessage(qsTr("Error running transactions: " + _message)); - onRunComplete: infoMessage(qsTr("Run complete")); - onNewBlock: infoMessage(qsTr("New block created")); + onRunStarted: infoMessage(qsTr("Running transactions..."), "run"); + onRunFailed: errorMessage(qsTr("Error running transactions: " + _message), "run"); + onRunComplete: infoMessage(qsTr("Run complete"), "run"); + onNewBlock: infoMessage(qsTr("New block created"), "state"); } Connections { target:projectModel - onDeploymentStarted: infoMessage(qsTr("Running deployment...")); - onDeploymentError: errorMessage(error); - onDeploymentComplete: infoMessage(qsTr("Deployment complete")); - onDeploymentStepChanged: infoMessage(message); + onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "deployment"); + onDeploymentError: errorMessage(error, "deployment"); + onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "deployment"); + onDeploymentStepChanged: infoMessage(message, "deployment"); } Connections { target: codeModel @@ -133,6 +146,53 @@ Rectangle { id: toolTipInfo tooltip: "" } + + Rectangle + { + function toggle() + { + if (logsContainer.state === "opened") + logsContainer.state = "closed" + else + logsContainer.state = "opened"; + } + + id: logsContainer + width: 1000 + height: 0 + anchors.topMargin: 2 + anchors.top: statusContainer.bottom + anchors.horizontalCenter: parent.horizontalCenter + visible: false + Component.onCompleted: + { + var top = logsContainer; + while (top.parent) + top = top.parent + var coordinates = logsContainer.mapToItem(top, 0, 0) + logsContainer.parent = top; + logsContainer.x = coordinates.x + logsContainer.y = coordinates.y + } + LogsPane + { + id: logPane + } + states: [ + State { + name: "opened"; + PropertyChanges { target: logsContainer; height: 500; visible: true } + }, + State { + name: "closed"; + PropertyChanges { target: logsContainer; height: 0; visible: false } + } + ] + transitions: Transition { + NumberAnimation { properties: "height"; easing.type: Easing.InOutQuad; duration: 200 } + NumberAnimation { properties: "visible"; easing.type: Easing.InOutQuad; duration: 200 } + } + } } Button @@ -140,7 +200,6 @@ Rectangle { id: logslink anchors.left: statusContainer.right anchors.leftMargin: 9 - visible: false anchors.verticalCenter: parent.verticalCenter action: displayLogAction iconSource: "qrc:/qml/img/search_filled.png" @@ -150,7 +209,9 @@ Rectangle { id: displayLogAction tooltip: qsTr("Display Log") onTriggered: { - mainContent.displayCompilationErrorIfAny(); + logsContainer.toggle(); + //if (status.state === "error" && logPane.front().type === "run") + // mainContent.displayCompilationErrorIfAny(); } } diff --git a/mix/qml/WebPreview.qml b/mix/qml/WebPreview.qml index 6f03088a4..5a9673a2c 100644 --- a/mix/qml/WebPreview.qml +++ b/mix/qml/WebPreview.qml @@ -12,6 +12,9 @@ Item { id: webPreview property string pendingPageUrl: "" property bool initialized: false + signal javaScriptErrorMessage(string _content) + signal javaScriptWarningMessage(string _content) + signal javaScriptInfoMessage(string _content) function setPreviewUrl(url) { if (!initialized) @@ -240,7 +243,13 @@ Item { id: webView experimental.settings.localContentCanAccessRemoteUrls: true onJavaScriptConsoleMessage: { - console.log(sourceID + ":" + lineNumber + ":" + message); + var info = sourceID + ":" + lineNumber + ":" + message; + if (level === 0) + webPreview.javaScriptInfoMessage(info); + else if (level === 1) + webPreview.javaScriptErrorMessage(info); + else if (level === 2) + webPreview.javaScriptErrorMessage(info); } onLoadingChanged: { if (!loading) { diff --git a/mix/qml/img/broom.png b/mix/qml/img/broom.png new file mode 100644 index 0000000000000000000000000000000000000000..76a9a0e0c90f9e671c3bbe3708f610c4a67f6383 GIT binary patch literal 501 zcmVl0knEFf(l4s2f?(imaF8NZjcWVO_}LI%a>%36+i~6T z^SQ6|3LtJhk^&Jz2utc^beF3b$swE(%#*EL^P}y!?)c0Gl3UrZ22;|P1>z-6qMhu) zSc&&J#k}OZ**r#`z41$?i>u|{BT{dCD|@ZL88aFoS+UGi|GK$H7_unV$Vw4NT7uEW zm-O+BfvnnV%>p9leRp*vN5T*w=e|-;#;Y22rS3<>&kwgnT?axGT}>SG8^x1s zXTOhJzePzd)sOKKbFkQxtFOOsE(7vswE3NB+UWECyT$!^v$yiJJ8C4)yjTAu`iq|U znj1Gy^<=#NA2xGJ-09bUCmJd(slT_!U2=U>^tx#e4LTY`t)UgF>el6QYc#rKzSe0f2{dxJC@=|jIIuvN`^^-?W_(@hk>V0_bL9qml/img/exit.png qml/img/run.png qml/img/note.png + qml/LogsPane.qml + qml/LogsWindow.qml + qml/img/copy.png + qml/img/broom.png diff --git a/mix/sortfilterproxymodel.cpp b/mix/sortfilterproxymodel.cpp new file mode 100644 index 000000000..75a50ea59 --- /dev/null +++ b/mix/sortfilterproxymodel.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "sortfilterproxymodel.h" +#include +#include + +using namespace dev::mix; + +SortFilterProxyModel::SortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged())); + connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged())); +} + +int SortFilterProxyModel::count() const +{ + return rowCount(); +} + +QObject *SortFilterProxyModel::source() const +{ + return sourceModel(); +} + +void SortFilterProxyModel::setSource(QObject *source) +{ + setSourceModel(qobject_cast(source)); +} + +QByteArray SortFilterProxyModel::sortRole() const +{ + return roleNames().value(QSortFilterProxyModel::sortRole()); +} + +void SortFilterProxyModel::setSortRole(const QByteArray &role) +{ + QSortFilterProxyModel::setSortRole(roleKey(role)); +} + +void SortFilterProxyModel::setSortOrder(Qt::SortOrder order) +{ + QSortFilterProxyModel::sort(0, order); +} + +QByteArray SortFilterProxyModel::filterRole() const +{ + return roleNames().value(QSortFilterProxyModel::filterRole()); +} + +void SortFilterProxyModel::setFilterRole(const QByteArray &role) +{ + QSortFilterProxyModel::setFilterRole(roleKey(role)); +} + +QString SortFilterProxyModel::filterString() const +{ + return filterRegExp().pattern(); +} + +void SortFilterProxyModel::setFilterString(const QString &filter) +{ + setFilterRegExp(QRegExp(filter, filterCaseSensitivity(), static_cast(filterSyntax()))); +} + +SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const +{ + return static_cast(filterRegExp().patternSyntax()); +} + +void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax syntax) +{ + setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast(syntax))); +} + +QJSValue SortFilterProxyModel::get(int idx) const +{ + QJSEngine *engine = qmlEngine(this); + QJSValue value = engine->newObject(); + if (idx >= 0 && idx < count()) { + QHash roles = roleNames(); + QHashIterator it(roles); + while (it.hasNext()) { + it.next(); + value.setProperty(QString::fromUtf8(it.value()), data(index(idx, 0), it.key()).toString()); + } + } + return value; +} + +int SortFilterProxyModel::roleKey(const QByteArray &role) const +{ + QHash roles = roleNames(); + QHashIterator it(roles); + while (it.hasNext()) { + it.next(); + if (it.value() == role) + return it.key(); + } + return -1; +} + +QHash SortFilterProxyModel::roleNames() const +{ + if (QAbstractItemModel *source = sourceModel()) + return source->roleNames(); + return QHash(); +} + +bool SortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + QRegExp rx = filterRegExp(); + if (rx.isEmpty()) + return true; + QAbstractItemModel *model = sourceModel(); + if (filterRole().isEmpty()) { + QHash roles = roleNames(); + QHashIterator it(roles); + while (it.hasNext()) { + it.next(); + QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); + QString key = model->data(sourceIndex, it.key()).toString(); + if (key.contains(rx)) + return true; + } + return false; + } + QModelIndex sourceIndex = model->index(sourceRow, 0, sourceParent); + if (!sourceIndex.isValid()) + return true; + QString key = model->data(sourceIndex, roleKey(filterRole())).toString(); + return key.contains(rx); +} diff --git a/mix/sortfilterproxymodel.h b/mix/sortfilterproxymodel.h new file mode 100644 index 000000000..2ff479413 --- /dev/null +++ b/mix/sortfilterproxymodel.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SORTFILTERPROXYMODEL_H +#define SORTFILTERPROXYMODEL_H + +#include +#include + +namespace dev +{ +namespace mix +{ + +class SortFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QObject *source READ source WRITE setSource) + + Q_PROPERTY(QByteArray sortRole READ sortRole WRITE setSortRole) + Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) + + Q_PROPERTY(QByteArray filterRole READ filterRole WRITE setFilterRole) + Q_PROPERTY(QString filterString READ filterString WRITE setFilterString) + Q_PROPERTY(FilterSyntax filterSyntax READ filterSyntax WRITE setFilterSyntax) + + Q_ENUMS(FilterSyntax) + +public: + explicit SortFilterProxyModel(QObject *parent = 0); + + QObject *source() const; + void setSource(QObject *source); + + QByteArray sortRole() const; + void setSortRole(const QByteArray &role); + + void setSortOrder(Qt::SortOrder order); + + QByteArray filterRole() const; + void setFilterRole(const QByteArray &role); + + QString filterString() const; + void setFilterString(const QString &filter); + + enum FilterSyntax { + RegExp, + Wildcard, + FixedString + }; + + FilterSyntax filterSyntax() const; + void setFilterSyntax(FilterSyntax syntax); + + int count() const; + Q_INVOKABLE QJSValue get(int index) const; + +signals: + void countChanged(); + +protected: + int roleKey(const QByteArray &role) const; + QHash roleNames() const; + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; +}; + +} +} +#endif // SORTFILTERPROXYMODEL_H