Browse Source

Merge pull request #1226 from yann300/logsManagement

mix - Logs window
cl-refactor
Gav Wood 10 years ago
parent
commit
2e66639253
  1. 3
      mix/AppContext.cpp
  2. 9
      mix/CodeEditorExtensionManager.cpp
  3. 2
      mix/CodeEditorExtensionManager.h
  4. 156
      mix/SortFilterProxyModel.cpp
  5. 97
      mix/SortFilterProxyModel.h
  6. 302
      mix/qml/LogsPane.qml
  7. 29
      mix/qml/LogsPaneStyle.qml
  8. 32
      mix/qml/MainContent.qml
  9. 170
      mix/qml/StatusPane.qml
  10. 7
      mix/qml/WebPreview.qml
  11. BIN
      mix/qml/img/broom.png
  12. BIN
      mix/qml/img/copy.png
  13. 1
      mix/qml/qmldir
  14. 4
      mix/res.qrc

3
mix/AppContext.cpp

@ -37,6 +37,7 @@
#include "QVariableDefinition.h" #include "QVariableDefinition.h"
#include "HttpServer.h" #include "HttpServer.h"
#include "AppContext.h" #include "AppContext.h"
#include "SortFilterProxyModel.h"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
@ -74,6 +75,7 @@ void AppContext::load()
qmlRegisterType<QBoolType>("org.ethereum.qml.QBoolType", 1, 0, "QBoolType"); qmlRegisterType<QBoolType>("org.ethereum.qml.QBoolType", 1, 0, "QBoolType");
qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration"); qmlRegisterType<QVariableDeclaration>("org.ethereum.qml.QVariableDeclaration", 1, 0, "QVariableDeclaration");
qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry"); qmlRegisterType<RecordLogEntry>("org.ethereum.qml.RecordLogEntry", 1, 0, "RecordLogEntry");
qmlRegisterType<SortFilterProxyModel>("org.ethereum.qml.SortFilterProxyModel", 1, 0, "SortFilterProxyModel");
QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml")); QQmlComponent projectModelComponent(m_applicationEngine, QUrl("qrc:/qml/ProjectModel.qml"));
QObject* projectModel = projectModelComponent.create(); QObject* projectModel = projectModelComponent.create();
if (projectModelComponent.isError()) if (projectModelComponent.isError())
@ -86,6 +88,7 @@ void AppContext::load()
m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel); m_applicationEngine->rootContext()->setContextProperty("projectModel", projectModel);
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer"); qmlRegisterType<HttpServer>("HttpServer", 1, 0, "HttpServer");
m_applicationEngine->load(QUrl("qrc:/qml/main.qml")); m_applicationEngine->load(QUrl("qrc:/qml/main.qml"));
QWindow *window = qobject_cast<QWindow*>(m_applicationEngine->rootObjects().at(0)); QWindow *window = qobject_cast<QWindow*>(m_applicationEngine->rootObjects().at(0));
window->setIcon(QIcon(":/res/mix_256x256x32.png")); window->setIcon(QIcon(":/res/mix_256x256x32.png"));

9
mix/CodeEditorExtensionManager.cpp

@ -51,14 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
return; return;
} }
void CodeEditorExtensionManager::initExtensions()
{
std::shared_ptr<StatusPane> output = std::make_shared<StatusPane>(m_appContext);
QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight);
initExtension(output);
}
void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext) void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
{ {
if (!_ext->contentUrl().isEmpty()) if (!_ext->contentUrl().isEmpty())
@ -93,5 +85,4 @@ void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView)
void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView) void CodeEditorExtensionManager::setHeaderView(QQuickItem* _headerView)
{ {
m_headerView = _headerView; m_headerView = _headerView;
initExtensions(); //TODO: move this to a proper place
} }

2
mix/CodeEditorExtensionManager.h

@ -49,8 +49,6 @@ class CodeEditorExtensionManager: public QObject
public: public:
CodeEditorExtensionManager(); CodeEditorExtensionManager();
~CodeEditorExtensionManager(); ~CodeEditorExtensionManager();
/// Initialize all extensions.
void initExtensions();
/// Initialize extension. /// Initialize extension.
void initExtension(std::shared_ptr<Extension>); void initExtension(std::shared_ptr<Extension>);
/// Set current tab view /// Set current tab view

156
mix/SortFilterProxyModel.cpp

@ -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();
}

97
mix/SortFilterProxyModel.h

@ -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";
};
}
}

302
mix/qml/LogsPane.qml

@ -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";
}
}
}
}
}

29
mix/qml/LogsPaneStyle.qml

@ -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"
}
}
}

32
mix/qml/MainContent.qml

@ -35,8 +35,8 @@ Rectangle {
onCompilationComplete: { onCompilationComplete: {
if (firstCompile) { if (firstCompile) {
firstCompile = false; firstCompile = false;
if (runOnProjectLoad) if (runOnProjectLoad)
startQuickDebugging(); startQuickDebugging();
} }
} }
} }
@ -102,7 +102,6 @@ Rectangle {
} }
CodeEditorExtensionManager { CodeEditorExtensionManager {
headerView: headerPaneTabs;
} }
Settings { Settings {
@ -116,6 +115,7 @@ Rectangle {
ColumnLayout ColumnLayout
{ {
id: mainColumn
anchors.fill: parent anchors.fill: parent
spacing: 0 spacing: 0
Rectangle { Rectangle {
@ -133,21 +133,15 @@ Rectangle {
} }
id: headerPaneContainer id: headerPaneContainer
anchors.fill: parent anchors.fill: parent
TabView { StatusPane
id: headerPaneTabs {
tabsVisible: false
antialiasing: true
anchors.fill: parent anchors.fill: parent
style: TabViewStyle { webPreview: webPreview
frameOverlap: 1
tab: Rectangle {}
frame: Rectangle { color: "transparent" }
}
} }
} }
} }
Rectangle{ Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
height: 1 height: 1
color: "#8c8c8c" color: "#8c8c8c"
@ -168,9 +162,9 @@ Rectangle {
{ {
anchors.fill: parent anchors.fill: parent
handleDelegate: Rectangle { handleDelegate: Rectangle {
width: 1 width: 1
height: 1 height: 1
color: "#8c8c8c" color: "#8c8c8c"
} }
orientation: Qt.Horizontal orientation: Qt.Horizontal
@ -180,16 +174,18 @@ Rectangle {
Layout.minimumWidth: 250 Layout.minimumWidth: 250
Layout.fillHeight: true Layout.fillHeight: true
} }
Rectangle { Rectangle {
id: contentView id: contentView
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
SplitView { SplitView {
handleDelegate: Rectangle { handleDelegate: Rectangle {
width: 1 width: 1
height: 1 height: 1
color: "#8c8c8c" color: "#8c8c8c"
} }
id: codeWebSplitter id: codeWebSplitter
anchors.fill: parent anchors.fill: parent
orientation: Qt.Vertical orientation: Qt.Vertical

170
mix/qml/StatusPane.qml

@ -8,6 +8,7 @@ import "."
Rectangle { Rectangle {
id: statusHeader id: statusHeader
objectName: "statusPane" objectName: "statusPane"
property variant webPreview
function updateStatus(message) function updateStatus(message)
{ {
@ -15,7 +16,6 @@ Rectangle {
{ {
status.state = ""; status.state = "";
status.text = qsTr("Compile successfully."); status.text = qsTr("Compile successfully.");
logslink.visible = false;
debugImg.state = "active"; debugImg.state = "active";
} }
else else
@ -23,39 +23,76 @@ Rectangle {
status.state = "error"; status.state = "error";
var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true); var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true);
status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail; status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail;
logslink.visible = true;
debugImg.state = ""; debugImg.state = "";
errorMessage(status.text, "Compilation");
} }
debugRunActionIcon.enabled = codeModel.hasContract; debugRunActionIcon.enabled = codeModel.hasContract;
} }
function infoMessage(text) function infoMessage(text, type)
{ {
status.state = ""; status.state = "";
status.text = text status.text = text
logslink.visible = false; logPane.push("info", type, text);
} }
function errorMessage(text) function warningMessage(text, type)
{
status.state = "warning";
status.text = text
logPane.push("warning", type, text);
}
function errorMessage(text, type)
{ {
status.state = "error"; status.state = "error";
status.text = text status.text = text
logslink.visible = false; logPane.push("error", type, text);
}
Connections {
target: webPreview
onJavaScriptMessage:
{
if (_level === 0)
infoMessage(_content, "JavaScript")
else
{
var message = _sourceId.substring(_sourceId.lastIndexOf("/") + 1) + " - " + qsTr("line") + " " + _lineNb + " - " + _content;
if (_level === 1)
warningMessage(message, "JavaScript")
else
errorMessage(message, "JavaScript")
}
}
} }
Connections { Connections {
target:clientModel target:clientModel
onRunStarted: infoMessage(qsTr("Running transactions...")); onRunStarted: infoMessage(qsTr("Running transactions..."), "Run");
onRunFailed: errorMessage(qsTr("Error running transactions: " + _message)); onRunFailed: errorMessage(format(_message), "Run");
onRunComplete: infoMessage(qsTr("Run complete")); onRunComplete: infoMessage(qsTr("Run complete"), "Run");
onNewBlock: infoMessage(qsTr("New block created")); onNewBlock: infoMessage(qsTr("New block created"), "State");
function format(_message)
{
var formatted = _message.match(/(?:<dev::eth::)(.+)(?:>)/);
if (formatted.length > 1)
formatted = formatted[1] + ": ";
else
return _message;
var exceptionInfos = _message.match(/(?:tag_)(.+)/g);
for (var k in exceptionInfos)
formatted += " " + exceptionInfos[k].replace("*]", "").replace("tag_", "").replace("=", "");
return formatted;
}
} }
Connections { Connections {
target:projectModel target:projectModel
onDeploymentStarted: infoMessage(qsTr("Running deployment...")); onDeploymentStarted: infoMessage(qsTr("Running deployment..."), "Deployment");
onDeploymentError: errorMessage(error); onDeploymentError: errorMessage(error, "Deployment");
onDeploymentComplete: infoMessage(qsTr("Deployment complete")); onDeploymentComplete: infoMessage(qsTr("Deployment complete"), "Deployment");
onDeploymentStepChanged: infoMessage(message); onDeploymentStepChanged: infoMessage(message, "Deployment");
} }
Connections { Connections {
target: codeModel target: codeModel
@ -74,6 +111,24 @@ Rectangle {
width: 500 width: 500
height: 30 height: 30
color: "#fcfbfc" color: "#fcfbfc"
states: [
State {
name: "logsOpened"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 1
}
},
State {
name: "logsClosed"
PropertyChanges {
target: statusContainer
border.color: "#5391d8"
border.width: 0
}
}
]
Text { Text {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -98,6 +153,17 @@ Rectangle {
target: statusContainer target: statusContainer
color: "#fffcd5" color: "#fffcd5"
} }
},
State {
name: "warning"
PropertyChanges {
target: status
color: "orange"
}
PropertyChanges {
target: statusContainer
color: "#fffcd5"
}
} }
] ]
onTextChanged: onTextChanged:
@ -127,30 +193,73 @@ Rectangle {
color: "transparent" color: "transparent"
} }
} }
MouseArea {
anchors.fill: parent
onClicked: {
logsContainer.toggle();
}
}
} }
Action { Action {
id: toolTipInfo id: toolTipInfo
tooltip: "" tooltip: ""
} }
}
Button Rectangle
{ {
id: logslink function toggle()
anchors.left: statusContainer.right {
anchors.leftMargin: 9 if (logsContainer.state === "opened")
visible: false {
anchors.verticalCenter: parent.verticalCenter statusContainer.state = "logsClosed";
action: displayLogAction logsContainer.state = "closed"
iconSource: "qrc:/qml/img/search_filled.png" }
} else
{
statusContainer.state = "logsOpened";
logsContainer.state = "opened";
logsContainer.focus = true;
forceActiveFocus();
}
}
Action { id: logsContainer
id: displayLogAction width: 1000
tooltip: qsTr("Display Log") height: 0
onTriggered: { anchors.topMargin: 10
mainContent.displayCompilationErrorIfAny(); anchors.top: statusContainer.bottom
anchors.horizontalCenter: parent.horizontalCenter
visible: false
radius: 5
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 }
}
} }
} }
@ -168,7 +277,6 @@ Rectangle {
{ {
color: "transparent" color: "transparent"
anchors.fill: parent anchors.fill: parent
Button Button
{ {
anchors.right: parent.right anchors.right: parent.right

7
mix/qml/WebPreview.qml

@ -12,6 +12,7 @@ Item {
id: webPreview id: webPreview
property string pendingPageUrl: "" property string pendingPageUrl: ""
property bool initialized: false property bool initialized: false
signal javaScriptMessage(var _level, string _sourceId, var _lineNb, string _content)
function setPreviewUrl(url) { function setPreviewUrl(url) {
if (!initialized) if (!initialized)
@ -198,7 +199,6 @@ Item {
{ {
setPreviewUrl(text); setPreviewUrl(text);
} }
focus: true focus: true
} }
@ -216,7 +216,9 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
width: 21 width: 21
height: 21 height: 21
focus: true
} }
CheckBox { CheckBox {
id: autoReloadOnSave id: autoReloadOnSave
checked: true checked: true
@ -227,6 +229,7 @@ Item {
text: qsTr("Auto reload on save") text: qsTr("Auto reload on save")
} }
} }
focus: true
} }
} }
} }
@ -240,7 +243,7 @@ Item {
id: webView id: webView
experimental.settings.localContentCanAccessRemoteUrls: true experimental.settings.localContentCanAccessRemoteUrls: true
onJavaScriptConsoleMessage: { onJavaScriptConsoleMessage: {
console.log(sourceID + ":" + lineNumber + ":" + message); webPreview.javaScriptMessage(level, sourceID, lineNumber, message);
} }
onLoadingChanged: { onLoadingChanged: {
if (!loading) { if (!loading) {

BIN
mix/qml/img/broom.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

BIN
mix/qml/img/copy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

1
mix/qml/qmldir

@ -5,3 +5,4 @@ singleton DebuggerPaneStyle 1.0 DebuggerPaneStyle.qml
singleton StateStyle 1.0 StateStyle.qml singleton StateStyle 1.0 StateStyle.qml
singleton StatusPaneStyle 1.0 StatusPaneStyle.qml singleton StatusPaneStyle 1.0 StatusPaneStyle.qml
singleton WebPreviewStyle 1.0 WebPreviewStyle.qml singleton WebPreviewStyle 1.0 WebPreviewStyle.qml
singleton LogsPaneStyle 1.0 LogsPaneStyle.qml

4
mix/res.qrc

@ -111,5 +111,9 @@
<file>qml/img/exit.png</file> <file>qml/img/exit.png</file>
<file>qml/img/run.png</file> <file>qml/img/run.png</file>
<file>qml/img/note.png</file> <file>qml/img/note.png</file>
<file>qml/LogsPane.qml</file>
<file>qml/img/copy.png</file>
<file>qml/img/broom.png</file>
<file>qml/LogsPaneStyle.qml</file>
</qresource> </qresource>
</RCC> </RCC>

Loading…
Cancel
Save