diff --git a/mix/ClientModel.cpp b/mix/ClientModel.cpp index eae3f8834..42fc4cfc3 100644 --- a/mix/ClientModel.cpp +++ b/mix/ClientModel.cpp @@ -351,7 +351,6 @@ void ClientModel::executeSequence(vector const& _sequence) if (!transaction.isFunctionCall) { callAddress(Address(address.toStdString()), bytes(), transaction); - onNewTransaction(); continue; } diff --git a/mix/CodeModel.h b/mix/CodeModel.h index e51eabf51..73835d807 100644 --- a/mix/CodeModel.h +++ b/mix/CodeModel.h @@ -33,6 +33,7 @@ #include #include #include +#include #include "SolidityType.h" #include "QBigInt.h" @@ -246,8 +247,12 @@ public: void gasEstimation(solidity::CompilerStack const& _cs); /// Gas cost by doc id Q_INVOKABLE QVariantList gasCostByDocumentId(QString const& _documentId) const; + /// Gas cost by @arg contractName @arg functionName Q_INVOKABLE QVariantList gasCostBy(QString const& _contractName, QString const& _functionName) const; + /// Set optimize code Q_INVOKABLE void setOptimizeCode(bool _value); + /// sha3 + Q_INVOKABLE QString sha3(QString _source) { return QString::fromStdString(dev::sha3(_source.toStdString()).hex()); } int txGas() { return static_cast(dev::eth::c_txGas); } int callStipend() { return static_cast(dev::eth::c_callStipend); } diff --git a/mix/qml/Application.qml b/mix/qml/Application.qml index 61a2e09b1..bdb0896e5 100644 --- a/mix/qml/Application.qml +++ b/mix/qml/Application.qml @@ -202,7 +202,7 @@ ApplicationWindow { text: qsTr("Deploy") shortcut: "F5" onTriggered: mainContent.startQuickDebugging() - enabled: codeModel.hasContract && !clientModel.running + enabled: codeModel.hasContract && !clientModel.running && projectModel.stateListModel.defaultStateName() !== "" } Action { diff --git a/mix/qml/BlockChain.qml b/mix/qml/BlockChain.qml index ccc027444..b6b5e3403 100644 --- a/mix/qml/BlockChain.qml +++ b/mix/qml/BlockChain.qml @@ -20,7 +20,7 @@ ColumnLayout { spacing: 0 property int previousWidth property variant debugTrRequested: [] - signal chainChanged + signal chainChanged(var blockIndex, var txIndex, var item) signal chainReloaded signal txSelected(var blockIndex, var txIndex) signal rebuilding @@ -30,15 +30,46 @@ ColumnLayout { { target: codeModel onContractRenamed: { - rebuild.startBlinking() + rebuild.needRebuild("ContractRenamed") } onNewContractCompiled: { - rebuild.startBlinking() + rebuild.needRebuild("NewContractCompiled") + } + onCompilationComplete: { + for (var c in rebuild.contractsHex) + { + if (codeModel.contracts[c] === undefined || codeModel.contracts[c].codeHex !== rebuild.contractsHex[c]) + { + if (!rebuild.containsRebuildCause("CodeChanged")) + { + rebuild.needRebuild("CodeChanged") + } + return + } + } + rebuild.notNeedRebuild("CodeChanged") } } + onChainChanged: { - rebuild.startBlinking() + if (rebuild.txSha3[blockIndex][txIndex] !== codeModel.sha3(JSON.stringify(model.blocks[blockIndex].transactions[txIndex]))) + { + rebuild.txChanged.push(rebuild.txSha3[blockIndex][txIndex]) + rebuild.needRebuild("txChanged") + } + else { + for (var k in rebuild.txChanged) + { + if (rebuild.txChanged[k] === rebuild.txSha3[blockIndex][txIndex]) + { + rebuild.txChanged.splice(k, 1) + break + } + } + if (rebuild.txChanged.length === 0) + rebuild.notNeedRebuild("txChanged") + } } onWidthChanged: @@ -174,7 +205,8 @@ ColumnLayout { Connections { target: block - onTxSelected: { + onTxSelected: + { blockChainPanel.txSelected(index, txIndex) } } @@ -274,8 +306,7 @@ ColumnLayout { Rectangle { Layout.preferredWidth: 100 - Layout.preferredHeight: 30 - + Layout.preferredHeight: 30 ScenarioButton { id: rebuild text: qsTr("Rebuild") @@ -283,6 +314,43 @@ ColumnLayout { height: 30 roundLeft: true roundRight: true + property variant contractsHex: ({}) + property variant txSha3: ({}) + property variant txChanged: [] + property var blinkReasons: [] + + function needRebuild(reason) + { + rebuild.startBlinking() + blinkReasons.push(reason) + } + + function containsRebuildCause(reason) + { + for (var c in blinkReasons) + { + if (blinkReasons[c] === reason) + { + return true + } + } + } + + + function notNeedRebuild(reason) + { + for (var c in blinkReasons) + { + if (blinkReasons[c] === reason) + { + blinkReasons.splice(c, 1) + break + } + } + if (blinkReasons.length === 0) + rebuild.stopBlinking() + } + onClicked: { if (ensureNotFuturetime.running) @@ -335,8 +403,34 @@ ColumnLayout { blockModel.append(model.blocks[j]) ensureNotFuturetime.start() + takeCodeSnapshot() + takeTxSnaphot() + blinkReasons = [] clientModel.setupScenario(model); } + + function takeCodeSnapshot() + { + contractsHex = {} + for (var c in codeModel.contracts) + contractsHex[c] = codeModel.contracts[c].codeHex + } + + function takeTxSnaphot() + { + txSha3 = {} + txChanged = [] + for (var j = 0; j < model.blocks.length; j++) + { + for (var k = 0; k < model.blocks[j].transactions.length; k++) + { + if (txSha3[j] === undefined) + txSha3[j] = {} + txSha3[j][k] = codeModel.sha3(JSON.stringify(model.blocks[j].transactions[k])) + } + } + } + buttonShortcut: "" sourceImg: "qrc:/qml/img/recycleicon@2x.png" } @@ -548,9 +642,8 @@ ColumnLayout { else { model.blocks[blockIndex].transactions[transactionIndex] = item blockModel.setTransaction(blockIndex, transactionIndex, item) - chainChanged() + chainChanged(blockIndex, transactionIndex, item) } - } } } diff --git a/mix/qml/ScenarioButton.qml b/mix/qml/ScenarioButton.qml index d4beaefaa..4c60682ca 100644 --- a/mix/qml/ScenarioButton.qml +++ b/mix/qml/ScenarioButton.qml @@ -30,8 +30,8 @@ Rectangle { id: left width: 10 height: parent.height - anchors.left: parent.left - anchors.leftMargin: -8 + anchors.left: contentRectangle.left + anchors.leftMargin: -4 radius: 15 } @@ -111,8 +111,8 @@ Rectangle { id: right width: 10 height: parent.height - anchors.left: contentRectangle.right - anchors.leftMargin: -8 + anchors.right: contentRectangle.right + anchors.rightMargin: -4 radius: 15 } diff --git a/mix/qml/ScenarioLoader.qml b/mix/qml/ScenarioLoader.qml index 58247b213..a54eb48bf 100644 --- a/mix/qml/ScenarioLoader.qml +++ b/mix/qml/ScenarioLoader.qml @@ -6,6 +6,7 @@ import QtQuick.Dialogs 1.2 import QtQuick.Window 2.0 import QtQuick.Dialogs 1.1 import Qt.labs.settings 1.0 +import org.ethereum.qml.InverseMouseArea 1.0 import "js/Debugger.js" as Debugger import "js/ErrorLocationFormater.js" as ErrorLocationFormater import "." @@ -18,6 +19,7 @@ ColumnLayout signal duplicated(variant scenario) signal loaded(variant scenario) signal renamed(variant scenario) + signal deleted() spacing: 0 function init() { @@ -29,146 +31,92 @@ ColumnLayout editStatus.visible = true } - Rectangle + RowLayout { - Layout.fillWidth: true - Layout.preferredHeight: 30 - color: "transparent" - Rectangle + Layout.preferredWidth: 560 + anchors.horizontalCenter: parent.horizontalCenter + Layout.preferredHeight: 60 + spacing: 0 + anchors.top: parent.top + anchors.topMargin: 10 + + Row { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - color: "transparent" - Label - { - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - id: scenarioName - font.bold: true - } + Layout.preferredWidth: 100 * 5 + 30 + Layout.preferredHeight: 50 + spacing: 25 - TextInput + Rectangle { - id: scenarioNameEdit - visible: false - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - Keys.onEnterPressed: - { - save() - } - - function edit() - { - editIconRect.anchors.left = scenarioNameEdit.right - editStatus.parent.anchors.left = scenarioNameEdit.right - scenarioNameEdit.forceActiveFocus() - } - - function save() - { - editIconRect.anchors.left = scenarioName.right - editStatus.parent.anchors.left = scenarioName.right - scenarioName.text = scenarioNameEdit.text - scenarioName.visible = true - scenarioNameEdit.visible = false - projectModel.stateListModel.getState(scenarioList.currentIndex).title = scenarioName.text - projectModel.saveProjectFile() - saved(state) - scenarioList.model.get(scenarioList.currentIndex).title = scenarioName.text - scenarioList.currentIndex = scenarioList.currentIndex - renamed(projectModel.stateListModel.getState(scenarioList.currentIndex)) - } - } + color: "white" + width: 251 + 30 + height: 30 - Connections - { - target: blockChainSelector - onLoaded: + Rectangle { - scenarioName.text = scenario.title - scenarioNameEdit.text = scenario.title - } - } - - Rectangle - { - anchors.left: scenarioName.right - anchors.top: scenarioName.top - anchors.leftMargin: 2 - Layout.preferredWidth: 20 - Text { - id: editStatus - text: "*" - visible: false + id: left + width: 10 + height: parent.height + anchors.left: parent.left + anchors.leftMargin: -5 + radius: 15 } - } - Rectangle - { - id: editIconRect - anchors.top: scenarioName.top - anchors.topMargin: 6 - anchors.left: scenarioName.right - anchors.leftMargin: 20 Image { source: "qrc:/qml/img/edittransaction.png" - width: 30 + height: parent.height - 10 fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter + anchors.left: parent.left + anchors.top: parent.top + anchors.topMargin: 4 + id: editImg MouseArea { anchors.fill: parent onClicked: { - scenarioName.visible = !scenarioName.visible - scenarioNameEdit.visible = !scenarioNameEdit.visible - if (!scenarioNameEdit.visible) - scenarioNameEdit.save() - else - scenarioNameEdit.edit() + scenarioNameEdit.toggleEdit() } } } - } - } - } - RowLayout - { - Layout.preferredWidth: 560 - anchors.horizontalCenter: parent.horizontalCenter - Layout.preferredHeight: 50 - spacing: 0 - - Row - { - Layout.preferredWidth: 100 * 5 - Layout.preferredHeight: 50 - spacing: 15 - - Rectangle - { - color: "transparent" - width: 251 - height: 30 - Rectangle + Label { - width: 10 + anchors.left: editImg.right + text: "X" height: parent.height - anchors.right: scenarioList.left - anchors.rightMargin: -8 - radius: 15 + color: "#cccccc" + id: deleteImg + anchors.top: parent.top + anchors.topMargin: 6 + visible: projectModel.stateListModel.count > 1 + MouseArea + { + anchors.fill: parent + onClicked: + { + if (projectModel.stateListModel.count > 1) + { + projectModel.stateListModel.deleteState(scenarioList.currentIndex) + scenarioList.currentIndex = 0 + deleted() + } + } + } } ComboBox { id: scenarioList + anchors.left: deleteImg.right + anchors.leftMargin: 2 model: projectModel.stateListModel + anchors.top: parent.top textRole: "title" height: parent.height width: 150 + signal updateView() + onCurrentIndexChanged: { restoreScenario.restore() @@ -177,15 +125,18 @@ ColumnLayout function load() { var state = projectModel.stateListModel.getState(currentIndex) - loaded(state) + if (state) + loaded(state) } style: ComboBoxStyle { + id: style background: Rectangle { color: "white" anchors.fill: parent } label: Rectangle { + property alias label: comboLabel anchors.fill: parent color: "white" Label { @@ -194,20 +145,33 @@ ColumnLayout elide: Text.ElideRight width: parent.width anchors.verticalCenter: parent.verticalCenter - anchors.horizontalCenter: parent.horizontalCenter - text: { - if (projectModel.stateListModel.getState(scenarioList.currentIndex)) - return projectModel.stateListModel.getState(scenarioList.currentIndex).title - else - return "" + anchors.left: parent.left + Component.onCompleted: + { + comboLabel.updateLabel() + } + + function updateLabel() + { + comboLabel.text = "" + if (scenarioList.currentIndex < projectModel.stateListModel.count) + comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title } + Connections { target: blockChainSelector onLoaded: { - comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title + if (projectModel.stateListModel.count > 0) + comboLabel.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title + else + return "" } onRenamed: { comboLabel.text = scenario.title + scenarioNameEdit.text = scenario.title + } + onDeleted: { + comboLabel.updateLabel() } } } @@ -215,9 +179,74 @@ ColumnLayout } } + TextField + { + id: scenarioNameEdit + anchors.left: deleteImg.right + anchors.leftMargin: 2 + height: parent.height + z: 5 + visible: false + width: 150 + Keys.onEnterPressed: + { + toggleEdit() + } + + Keys.onReturnPressed: + { + toggleEdit() + } + + function toggleEdit() + { + scenarioList.visible = !scenarioList.visible + scenarioNameEdit.visible = !scenarioNameEdit.visible + if (!scenarioNameEdit.visible) + scenarioNameEdit.save() + else + { + scenarioNameEdit.text = projectModel.stateListModel.getState(scenarioList.currentIndex).title + scenarioNameEdit.forceActiveFocus() + outsideClick.active = true + } + } + + function save() + { + outsideClick.active = false + projectModel.stateListModel.getState(scenarioList.currentIndex).title = scenarioNameEdit.text + projectModel.saveProjectFile() + saved(state) + scenarioList.model.get(scenarioList.currentIndex).title = scenarioNameEdit.text + scenarioList.currentIndex = scenarioList.currentIndex + renamed(projectModel.stateListModel.getState(scenarioList.currentIndex)) + } + + style: TextFieldStyle { + background: Rectangle { + radius: 2 + implicitWidth: 100 + implicitHeight: 30 + color: "white" + border.color: "#cccccc" + border.width: 1 + } + } + + InverseMouseArea { + id: outsideClick + anchors.fill: parent + active: false + onClickedOutside: { + scenarioNameEdit.toggleEdit() + } + } + } + Rectangle { - width: 1 + width: 1 height: parent.height anchors.right: addScenario.left color: "#ededed" @@ -236,7 +265,6 @@ ColumnLayout projectModel.stateListModel.appendState(item) projectModel.stateListModel.save() scenarioList.currentIndex = projectModel.stateListModel.count - 1 - scenarioNameEdit.edit() } text: qsTr("New..") roundRight: true @@ -251,15 +279,6 @@ ColumnLayout height: 30 color: "transparent" - Rectangle - { - width: 10 - height: parent.height - anchors.right: restoreScenario.left - anchors.rightMargin: -4 - radius: 15 - } - ScenarioButton { id: restoreScenario width: 100 @@ -275,7 +294,6 @@ ColumnLayout var state = projectModel.stateListModel.reloadStateFromFromProject(scenarioList.currentIndex) if (state) { - editStatus.visible = false restored(state) loaded(state) } diff --git a/mix/qml/StateListModel.qml b/mix/qml/StateListModel.qml index d3bf1c860..e7b092463 100644 --- a/mix/qml/StateListModel.qml +++ b/mix/qml/StateListModel.qml @@ -403,7 +403,10 @@ Item { function defaultStateName() { - return stateList[defaultStateIndex].title; + if (stateList.length > 0) + return stateList[defaultStateIndex].title; + else + return "" } function reloadStateFromFromProject(index) diff --git a/mix/qml/Watchers.qml b/mix/qml/Watchers.qml index 9625d9424..654670d6c 100644 --- a/mix/qml/Watchers.qml +++ b/mix/qml/Watchers.qml @@ -83,7 +83,7 @@ Rectangle { Label { id: fromLabel text: qsTr("from") - visible: from.text !== "" + visible: false color: "#EAB920" } Label { @@ -97,7 +97,7 @@ Rectangle { Label { id: toLabel text: qsTr("to") - visible: from.text !== "" + visible: false color: "#EAB920" } Label {