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

9
mix/CodeEditorExtensionManager.cpp

@ -51,14 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
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)
{
if (!_ext->contentUrl().isEmpty())
@ -93,5 +85,4 @@ void CodeEditorExtensionManager::setRightView(QQuickItem* _rightView)
void CodeEditorExtensionManager::setHeaderView(QQuickItem* _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:
CodeEditorExtensionManager();
~CodeEditorExtensionManager();
/// Initialize all extensions.
void initExtensions();
/// Initialize extension.
void initExtension(std::shared_ptr<Extension>);
/// 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: {
if (firstCompile) {
firstCompile = false;
if (runOnProjectLoad)
startQuickDebugging();
if (runOnProjectLoad)
startQuickDebugging();
}
}
}
@ -102,7 +102,6 @@ Rectangle {
}
CodeEditorExtensionManager {
headerView: headerPaneTabs;
}
Settings {
@ -116,6 +115,7 @@ Rectangle {
ColumnLayout
{
id: mainColumn
anchors.fill: parent
spacing: 0
Rectangle {
@ -133,21 +133,15 @@ 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
}
}
}
Rectangle{
Rectangle {
Layout.fillWidth: true
height: 1
color: "#8c8c8c"
@ -168,9 +162,9 @@ Rectangle {
{
anchors.fill: parent
handleDelegate: Rectangle {
width: 1
height: 1
color: "#8c8c8c"
width: 1
height: 1
color: "#8c8c8c"
}
orientation: Qt.Horizontal
@ -180,16 +174,18 @@ Rectangle {
Layout.minimumWidth: 250
Layout.fillHeight: true
}
Rectangle {
id: contentView
Layout.fillHeight: true
Layout.fillWidth: true
SplitView {
handleDelegate: Rectangle {
handleDelegate: Rectangle {
width: 1
height: 1
color: "#8c8c8c"
}
}
id: codeWebSplitter
anchors.fill: parent
orientation: Qt.Vertical

170
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,76 @@ Rectangle {
status.state = "error";
var errorInfo = ErrorLocationFormater.extractErrorInfo(message, true);
status.text = errorInfo.errorLocation + " " + errorInfo.errorDetail;
logslink.visible = true;
debugImg.state = "";
errorMessage(status.text, "Compilation");
}
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 = "warning";
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
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 {
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(format(_message), "Run");
onRunComplete: infoMessage(qsTr("Run complete"), "Run");
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 {
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
@ -74,6 +111,24 @@ Rectangle {
width: 500
height: 30
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 {
anchors.verticalCenter: parent.verticalCenter
@ -98,6 +153,17 @@ Rectangle {
target: statusContainer
color: "#fffcd5"
}
},
State {
name: "warning"
PropertyChanges {
target: status
color: "orange"
}
PropertyChanges {
target: statusContainer
color: "#fffcd5"
}
}
]
onTextChanged:
@ -127,30 +193,73 @@ Rectangle {
color: "transparent"
}
}
MouseArea {
anchors.fill: parent
onClicked: {
logsContainer.toggle();
}
}
}
Action {
id: toolTipInfo
tooltip: ""
}
}
Button
{
id: logslink
anchors.left: statusContainer.right
anchors.leftMargin: 9
visible: false
anchors.verticalCenter: parent.verticalCenter
action: displayLogAction
iconSource: "qrc:/qml/img/search_filled.png"
}
Rectangle
{
function toggle()
{
if (logsContainer.state === "opened")
{
statusContainer.state = "logsClosed";
logsContainer.state = "closed"
}
else
{
statusContainer.state = "logsOpened";
logsContainer.state = "opened";
logsContainer.focus = true;
forceActiveFocus();
}
}
Action {
id: displayLogAction
tooltip: qsTr("Display Log")
onTriggered: {
mainContent.displayCompilationErrorIfAny();
id: logsContainer
width: 1000
height: 0
anchors.topMargin: 10
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"
anchors.fill: parent
Button
{
anchors.right: parent.right

7
mix/qml/WebPreview.qml

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

Loading…
Cancel
Save