Gav Wood
10 years ago
14 changed files with 750 additions and 62 deletions
@ -0,0 +1,156 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Yann <yann@ethdev.com> |
|||
* @date 2015 |
|||
* Proxy used to filter a QML TableView. |
|||
*/ |
|||
|
|||
|
|||
#include "SortFilterProxyModel.h" |
|||
#include <QtDebug> |
|||
#include <QtQml> |
|||
|
|||
using namespace dev::mix; |
|||
|
|||
SortFilterProxyModel::SortFilterProxyModel(QObject* _parent) : QSortFilterProxyModel(_parent) |
|||
{ |
|||
connect(this, &SortFilterProxyModel::rowsInserted, this, &SortFilterProxyModel::countChanged); |
|||
connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged); |
|||
} |
|||
|
|||
int SortFilterProxyModel::count() const |
|||
{ |
|||
return rowCount(); |
|||
} |
|||
|
|||
QObject* SortFilterProxyModel::source() const |
|||
{ |
|||
return sourceModel(); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setSource(QObject* _source) |
|||
{ |
|||
setSourceModel(qobject_cast<QAbstractItemModel*>(_source)); |
|||
} |
|||
|
|||
QByteArray SortFilterProxyModel::sortRole() const |
|||
{ |
|||
return roleNames().value(QSortFilterProxyModel::sortRole()); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setSortRole(QByteArray const& _role) |
|||
{ |
|||
QSortFilterProxyModel::setSortRole(roleKey(_role)); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setSortOrder(Qt::SortOrder _order) |
|||
{ |
|||
QSortFilterProxyModel::sort(0, _order); |
|||
} |
|||
|
|||
QString SortFilterProxyModel::filterString() const |
|||
{ |
|||
return filterRegExp().pattern(); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setFilterString(QString const& _filter) |
|||
{ |
|||
setFilterRegExp(QRegExp(_filter, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax()))); |
|||
} |
|||
|
|||
SortFilterProxyModel::FilterSyntax SortFilterProxyModel::filterSyntax() const |
|||
{ |
|||
return static_cast<FilterSyntax>(filterRegExp().patternSyntax()); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setFilterSyntax(SortFilterProxyModel::FilterSyntax _syntax) |
|||
{ |
|||
setFilterRegExp(QRegExp(filterString(), filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(_syntax))); |
|||
} |
|||
|
|||
QJSValue SortFilterProxyModel::get(int _idx) const |
|||
{ |
|||
QJSEngine *engine = qmlEngine(this); |
|||
QJSValue value = engine->newObject(); |
|||
if (_idx >= 0 && _idx < count()) |
|||
{ |
|||
QHash<int, QByteArray> roles = roleNames(); |
|||
QHashIterator<int, QByteArray> 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(QByteArray const& _role) const |
|||
{ |
|||
QHash<int, QByteArray> roles = roleNames(); |
|||
QHashIterator<int, QByteArray> it(roles); |
|||
while (it.hasNext()) |
|||
{ |
|||
it.next(); |
|||
if (it.value() == _role) |
|||
return it.key(); |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
QHash<int, QByteArray> SortFilterProxyModel::roleNames() const |
|||
{ |
|||
if (QAbstractItemModel* source = sourceModel()) |
|||
return source->roleNames(); |
|||
return QHash<int, QByteArray>(); |
|||
} |
|||
|
|||
bool SortFilterProxyModel::filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const |
|||
{ |
|||
QAbstractItemModel* model = sourceModel(); |
|||
QModelIndex sourceIndex = model->index(_sourceRow, 0, _sourceParent); |
|||
if (!sourceIndex.isValid()) |
|||
return true; |
|||
|
|||
QString keyType = model->data(sourceIndex, roleKey(type.toUtf8())).toString(); |
|||
QString keyContent = model->data(sourceIndex, roleKey(content.toUtf8())).toString(); |
|||
return keyType.contains(m_filterType) && keyContent.contains(m_filterContent); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setFilterType(QString const& _type) |
|||
{ |
|||
m_filterType = QRegExp(_type, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())); |
|||
setFilterRegExp(_type); |
|||
} |
|||
|
|||
QString SortFilterProxyModel::filterType() const |
|||
{ |
|||
return m_filterType.pattern(); |
|||
} |
|||
|
|||
void SortFilterProxyModel::setFilterContent(QString const& _content) |
|||
{ |
|||
m_filterContent = QRegExp(_content, filterCaseSensitivity(), static_cast<QRegExp::PatternSyntax>(filterSyntax())); |
|||
setFilterRegExp(_content); |
|||
} |
|||
|
|||
QString SortFilterProxyModel::filterContent() const |
|||
{ |
|||
return m_filterContent.pattern(); |
|||
} |
|||
|
@ -0,0 +1,97 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Yann <yann@ethdev.com> |
|||
* @date 2015 |
|||
* Proxy used to filter a QML TableView. |
|||
*/ |
|||
|
|||
|
|||
#pragma once |
|||
|
|||
#include <QtCore/qsortfilterproxymodel.h> |
|||
#include <QtQml/qjsvalue.h> |
|||
|
|||
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(QString filterContent READ filterContent WRITE setFilterContent) |
|||
Q_PROPERTY(QString filterType READ filterType WRITE setFilterType) |
|||
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(QByteArray const& _role); |
|||
|
|||
void setSortOrder(Qt::SortOrder _order); |
|||
|
|||
QString filterContent() const; |
|||
void setFilterContent(QString const& _content); |
|||
QString filterType() const; |
|||
void setFilterType(QString const& _type); |
|||
|
|||
QString filterString() const; |
|||
void setFilterString(QString const& _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(QByteArray const& _role) const; |
|||
QHash<int, QByteArray> roleNames() const; |
|||
bool filterAcceptsRow(int _sourceRow, QModelIndex const& _sourceParent) const; |
|||
|
|||
private: |
|||
QRegExp m_filterType; |
|||
QRegExp m_filterContent; |
|||
const QString type = "type"; |
|||
const QString content = "content"; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,302 @@ |
|||
import QtQuick 2.0 |
|||
import QtQuick.Layouts 1.0 |
|||
import QtQuick.Controls 1.1 |
|||
import QtQuick.Controls.Styles 1.3 |
|||
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: LogsPaneStyle.generic.layout.backgroundColor |
|||
border.color: LogsPaneStyle.generic.layout.borderColor |
|||
border.width: LogsPaneStyle.generic.layout.borderWidth |
|||
ColumnLayout { |
|||
z: 2 |
|||
height: parent.height |
|||
width: parent.width |
|||
spacing: 0 |
|||
Row |
|||
{ |
|||
id: rowAction |
|||
Layout.preferredHeight: LogsPaneStyle.generic.layout.headerHeight |
|||
height: LogsPaneStyle.generic.layout.headerHeight |
|||
anchors.leftMargin: LogsPaneStyle.generic.layout.leftMargin |
|||
anchors.left: parent.left |
|||
spacing: LogsPaneStyle.generic.layout.headerButtonSpacing |
|||
Button |
|||
{ |
|||
height: LogsPaneStyle.generic.layout.headerButtonHeight |
|||
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: LogsPaneStyle.generic.layout.headerButtonHeight |
|||
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 + "\n"; |
|||
} |
|||
appContext.toClipboard(content); |
|||
} |
|||
} |
|||
|
|||
Rectangle { |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: 1; |
|||
height: parent.height - 10 |
|||
color : "#808080" |
|||
} |
|||
|
|||
ToolButton { |
|||
id: javascriptButton |
|||
checkable: true |
|||
height: LogsPaneStyle.generic.layout.headerButtonHeight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
checked: true |
|||
onCheckedChanged: { |
|||
proxyModel.toogleFilter("javascript") |
|||
} |
|||
tooltip: qsTr("JavaScript") |
|||
style: |
|||
ButtonStyle { |
|||
label: |
|||
Item { |
|||
DefaultLabel { |
|||
font.family: LogsPaneStyle.generic.layout.logLabelFont |
|||
font.pointSize: Style.absoluteSize(-3) |
|||
color: LogsPaneStyle.generic.layout.logLabelColor |
|||
anchors.centerIn: parent |
|||
text: qsTr("JavaScript") |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
ToolButton { |
|||
id: runButton |
|||
checkable: true |
|||
height: LogsPaneStyle.generic.layout.headerButtonHeight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
checked: true |
|||
onCheckedChanged: { |
|||
proxyModel.toogleFilter("run") |
|||
} |
|||
tooltip: qsTr("Run") |
|||
style: |
|||
ButtonStyle { |
|||
label: |
|||
Item { |
|||
DefaultLabel { |
|||
font.family: LogsPaneStyle.generic.layout.logLabelFont |
|||
font.pointSize: Style.absoluteSize(-3) |
|||
color: LogsPaneStyle.generic.layout.logLabelColor |
|||
anchors.centerIn: parent |
|||
text: qsTr("Run") |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
ToolButton { |
|||
id: stateButton |
|||
checkable: true |
|||
height: LogsPaneStyle.generic.layout.headerButtonHeight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
checked: true |
|||
onCheckedChanged: { |
|||
proxyModel.toogleFilter("state") |
|||
} |
|||
tooltip: qsTr("State") |
|||
style: |
|||
ButtonStyle { |
|||
label: |
|||
Item { |
|||
DefaultLabel { |
|||
font.family: LogsPaneStyle.generic.layout.logLabelFont |
|||
font.pointSize: Style.absoluteSize(-3) |
|||
color: "#5391d8" |
|||
anchors.centerIn: parent |
|||
text: qsTr("State") |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
ToolButton { |
|||
id: compilationButton |
|||
checkable: true |
|||
height: LogsPaneStyle.generic.layout.headerButtonHeight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
checked: false |
|||
onCheckedChanged: { |
|||
proxyModel.toogleFilter("compilation") |
|||
} |
|||
tooltip: qsTr("Compilation") |
|||
style: |
|||
ButtonStyle { |
|||
label: |
|||
Item { |
|||
DefaultLabel { |
|||
font.family: LogsPaneStyle.generic.layout.logLabelFont |
|||
font.pointSize: Style.absoluteSize(-3) |
|||
color: "#5391d8" |
|||
anchors.centerIn: parent |
|||
text: qsTr("Compilation") |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
DefaultTextField |
|||
{ |
|||
id: searchBox |
|||
height: LogsPaneStyle.generic.layout.headerButtonHeight |
|||
anchors.verticalCenter: parent.verticalCenter |
|||
width: LogsPaneStyle.generic.layout.headerInputWidth |
|||
font.family: LogsPaneStyle.generic.layout.logLabelFont |
|||
font.pointSize: Style.absoluteSize(-3) |
|||
font.italic: true |
|||
onTextChanged: { |
|||
proxyModel.search(text); |
|||
} |
|||
} |
|||
} |
|||
|
|||
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)); |
|||
if (log) |
|||
appContext.toClipboard(log.type + "\t" + log.level + "\t" + log.date + "\t" + log.content); |
|||
} |
|||
|
|||
model: SortFilterProxyModel { |
|||
id: proxyModel |
|||
source: logsModel |
|||
property var roles: ["-", "javascript", "run", "state"] |
|||
|
|||
Component.onCompleted: { |
|||
filterType = regEx(proxyModel.roles); |
|||
} |
|||
|
|||
function search(_value) |
|||
{ |
|||
filterContent = _value; |
|||
} |
|||
|
|||
function toogleFilter(_value) |
|||
{ |
|||
var count = roles.length; |
|||
for (var i in roles) |
|||
{ |
|||
if (roles[i] === _value) |
|||
{ |
|||
roles.splice(i, 1); |
|||
break; |
|||
} |
|||
} |
|||
if (count === roles.length) |
|||
roles.push(_value); |
|||
|
|||
filterType = regEx(proxyModel.roles); |
|||
} |
|||
|
|||
function regEx(_value) |
|||
{ |
|||
return "(?:" + roles.join('|') + ")"; |
|||
} |
|||
|
|||
filterType: "(?:javascript|run|state)" |
|||
filterContent: "" |
|||
filterSyntax: SortFilterProxyModel.RegExp |
|||
filterCaseSensitivity: Qt.CaseInsensitive |
|||
} |
|||
TableViewColumn |
|||
{ |
|||
role: "date" |
|||
title: qsTr("date") |
|||
width: LogsPaneStyle.generic.layout.dateWidth |
|||
delegate: itemDelegate |
|||
} |
|||
TableViewColumn |
|||
{ |
|||
role: "type" |
|||
title: qsTr("type") |
|||
width: LogsPaneStyle.generic.layout.typeWidth |
|||
delegate: itemDelegate |
|||
} |
|||
TableViewColumn |
|||
{ |
|||
role: "content" |
|||
title: qsTr("content") |
|||
width: LogsPaneStyle.generic.layout.contentWidth |
|||
delegate: itemDelegate |
|||
} |
|||
|
|||
rowDelegate: Item { |
|||
Rectangle { |
|||
width: logsTable.width - 4 |
|||
height: 17 |
|||
color: styleData.alternate ? "transparent" : LogsPaneStyle.generic.layout.logAlternateColor |
|||
} |
|||
} |
|||
} |
|||
|
|||
Component { |
|||
id: itemDelegate |
|||
DefaultLabel { |
|||
text: styleData.value; |
|||
font.family: LogsPaneStyle.generic.layout.logLabelFont |
|||
font.pointSize: Style.absoluteSize(-1) |
|||
color: { |
|||
if (proxyModel.get(styleData.row).level === "error") |
|||
return "red"; |
|||
else if (proxyModel.get(styleData.row).level === "warning") |
|||
return "orange"; |
|||
else |
|||
return "#808080"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
pragma Singleton |
|||
import QtQuick 2.0 |
|||
|
|||
QtObject { |
|||
|
|||
function absoluteSize(rel) |
|||
{ |
|||
return systemPointSize + rel; |
|||
} |
|||
|
|||
property QtObject generic: QtObject { |
|||
property QtObject layout: QtObject { |
|||
property string backgroundColor: "#f7f7f7" |
|||
property string borderColor: "#5391d8" |
|||
property int borderWidth: 1 |
|||
property int headerHeight: 35 |
|||
property int headerButtonSpacing: 5 |
|||
property int leftMargin: 10 |
|||
property int headerButtonHeight: 30 |
|||
property string logLabelColor: "#5391d8" |
|||
property string logLabelFont: "sans serif" |
|||
property int headerInputWidth: 200 |
|||
property int dateWidth: 150 |
|||
property int typeWidth: 80 |
|||
property int contentWidth: 700 |
|||
property string logAlternateColor: "#f0f0f0" |
|||
} |
|||
} |
|||
} |
After Width: | Height: | Size: 501 B |
After Width: | Height: | Size: 342 B |
Loading…
Reference in new issue