diff --git a/mix/MixApplication.cpp b/mix/MixApplication.cpp index b5226b90c..1a55b1e47 100644 --- a/mix/MixApplication.cpp +++ b/mix/MixApplication.cpp @@ -33,7 +33,7 @@ MixApplication::MixApplication(int _argc, char* _argv[]): QApplication(_argc, _argv), m_engine(new QQmlApplicationEngine()), m_appContext(new AppContext(m_engine.get())) { qmlRegisterType("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager"); - QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff + //QObject::connect(this, SIGNAL(lastWindowClosed()), context(), SLOT(quitApplication())); //use to kill ApplicationContext and other stuff m_engine->load(QUrl("qrc:/qml/main.qml")); //m_engine->load(QUrl("qrc:/qml/ProjectModel.qml")); m_appContext->loadProject(); diff --git a/mix/qml.qrc b/mix/qml.qrc index 53a2d1ae7..0e25de765 100644 --- a/mix/qml.qrc +++ b/mix/qml.qrc @@ -15,8 +15,8 @@ qml/js/Debugger.js qml/NewProjectDialog.qml qml/ProjectModel.qml - qml/CodeEditorModel.qml qml/CodeEditor.qml qml/CodeEditorView.qml + qml/js/ProjectModel.js diff --git a/mix/qml/CodeEditorModel.qml b/mix/qml/CodeEditorModel.qml deleted file mode 100644 index c696272ce..000000000 --- a/mix/qml/CodeEditorModel.qml +++ /dev/null @@ -1,13 +0,0 @@ -pragma Singleton - -import QtQuick 2.0 -import QtQuick.Window 2.0 -import QtQuick.Layouts 1.0 -import QtQuick.Controls 1.0 -import QtQuick.Dialogs 1.1 - -Item { - id: codeEditorModel - - property var codeDocuments: [] -} diff --git a/mix/qml/CodeEditorView.qml b/mix/qml/CodeEditorView.qml index 9edc81ed9..45d2cfb2a 100644 --- a/mix/qml/CodeEditorView.qml +++ b/mix/qml/CodeEditorView.qml @@ -11,7 +11,7 @@ Item { function getDocumentText(documentId) { for (i = 0; i < editorListModel.count; i++) { if (editorListModel.get(i).documentId === documentId) { - return editors.itemAt(i).getDocumentText(); + return editors.itemAt(i).getText(); } } return ""; @@ -41,9 +41,20 @@ Item { Connections { target: ProjectModel - onDocumentOpen: { + onDocumentOpened: { openDocument(document); } + onProjectSaved: { + for (var i = 0; i < editorListModel.count; i++) + fileIo.writeFile(editorListModel.get(i).path, editors.itemAt(i).item.getText()); + } + onProjectClosed: { + for (var i = 0; i < editorListModel.count; i++) { + editors.itemAt(i).visible = false; + } + editorListModel.clear(); + currentDocumentId = ""; + } } CodeEditor { @@ -58,7 +69,7 @@ Item { asynchronous: true anchors.fill: parent sourceComponent: codeEditor - visible: (currentDocumentId === editorListModel.get(index).documentId) + visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId) onVisibleChanged: { loadIfNotLoaded() } diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 77810931c..1f578fc6a 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -5,19 +5,36 @@ import QtQuick.Controls 1.0 import org.ethereum.qml.ProjectModel 1.0 Item { - ListView { - id: projectList - model: ProjectModel.listModel + + ColumnLayout { anchors.fill: parent - delegate: renderDelegate - highlight: Rectangle { - color: "lightsteelblue"; + Text { + Layout.fillWidth: true + color: "blue" + text: ProjectModel.projectData ? ProjectModel.projectData.title : "" + horizontalAlignment: Text.AlignHCenter + visible: !ProjectModel.isEmpty; } - highlightFollowsCurrentItem: true - focus: true - clip: true - } + ListView { + Layout.fillWidth: true + Layout.fillHeight: true + id: projectList + model: ProjectModel.listModel + + delegate: renderDelegate + highlight: Rectangle { + color: "lightsteelblue"; + } + highlightFollowsCurrentItem: true + focus: true + clip: true + onCurrentIndexChanged: { + if (currentIndex >= 0 && currentIndex < ProjectModel.listModel.count) + ProjectModel.openDocument(ProjectModel.listModel.get(currentIndex).documentId); + } + } + } Component { id: renderDelegate Item { @@ -42,10 +59,16 @@ Item { onClicked:{ projectList.currentIndex = index; - ProjectModel.documentOpen(ProjectModel.listModel.get(index)); + } + } + + Connections { + target: ProjectModel + onProjectLoaded: { + projectList.currentIndex = 0; } } } } - } + diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index f8217353d..976f70316 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -7,119 +7,33 @@ import QtQuick.Controls 1.0 import QtQuick.Dialogs 1.1 import Qt.labs.settings 1.0 +import "js/ProjectModel.js" as ProjectModelCode + Item { id: projectModel signal projectClosed signal projectLoaded - signal documentOpen(var document) + signal documentOpened(var document) + signal projectSaved - property bool isEmpty: (projectFile === "") + property bool isEmpty: (projectData === null) readonly property string projectFileName: ".mix" property bool haveUnsavedChanges: false - property string projectFile: "" + property string projectPath: "" property var projectData: null property var listModel: projectListModel - function saveAll() { - saveProject(); - } - - function createProject() { - closeProject(); - newProjectDialog.open(); - } - - function browseProject() { - openProjectFileDialog.open(); - } - - function closeProject() { - if (!isEmpty) { - console.log("closing project"); - if (haveUnsavedChanges) - saveMessageDialog.open(); - else - doCloseProject(); - } - } - - function saveProject() { - if (!isEmpty) { - var json = JSON.stringify(projectData); - fileIo.writeFile(projectFile, json); - } - } - - function loadProject(path) { - closeProject(); - console.log("loading project at " + path); - var json = fileIo.readFile(path); - projectData = JSON.parse(json); - projectFile = path; - if (!projectData.files) - projectData.files = []; - - for(var i = 0; i < projectData.files.length; i++) { - var p = projectData.files[i]; - addFile(p); - } - projectSettings.lastProjectPath = projectFile; - projectLoaded(); - } - - function addExistingFile() { - addExistingFileDialog().open(); - } - - function addProjectFiles(files) { - for(var i = 0; i < files.length; i++) - addFile(files[i]); - } - - function addFile(file) { - var p = file; - var fileData = { - contract: false, - path: p, - name: p.substring(p.lastIndexOf("/") + 1, p.length), - documentId: p, - isText: true, - isContract: p.substring(p.length - 4, p.length) === ".sol", - }; - - projectListModel.append(fileData); - } - - function doCloseProject() { - projectListModel.clear(); - projectFile = ""; - projectData = null; - projectClosed(); - } - - function doCreateProject(title, path) { - closeProject(); - console.log("creating project " + title + " at " + path); - if (path[path.length - 1] !== "/") - path += "/"; - var dirPath = path + title; - fileIo.makeDir(dirPath); - var projectFile = dirPath + "/" + projectFileName; - - var indexFile = dirPath + "/index.html"; - var contractsFile = dirPath + "/contracts.sol"; - var projectData = { - files: [ indexFile, contractsFile ] - }; - - fileIo.writeFile(indexFile, ""); - fileIo.writeFile(contractsFile, "contract MyContract {}"); - var json = JSON.stringify(projectData); - fileIo.writeFile(projectFile, json); - loadProject(projectFile); - } + //interface + function saveAll() { ProjectModelCode.saveAll(); } + function createProject() { ProjectModelCode.createProject(); } + function browseProject() { ProjectModelCode.browseProject(); } + function closeProject() { ProjectModelCode.closeProject(); } + function saveProject() { ProjectModelCode.saveProject(); } + function loadProject(path) { ProjectModelCode.loadProject(path); } + function addExistingFile() { ProjectModelCode.addExistingFile(); } + function openDocument(documentId) { ProjectModelCode.openDocument(documentId); } Component.onCompleted: { if (projectSettings.lastProjectPath) @@ -132,7 +46,7 @@ Item { onAccepted: { var title = newProjectDialog.projectTitle; var path = newProjectDialog.projectPath; - doCreateProject(title, path); + ProjectModelCode.doCreateProject(title, path); } } @@ -144,10 +58,10 @@ Item { icon: StandardIcon.Question onAccepted: { projectModel.saveAll(); - projectModel.doCloseProject(); + ProjectModelCode.doCloseProject(); } onRejected: { - projectModel.doCloseProject(); + ProjectModelCode.doCloseProject(); } } @@ -167,8 +81,8 @@ Item { selectFolder: true onAccepted: { var path = openProjectFileDialog.fileUrl.toString(); - path += "/" + projectFileName; - loadProject(path); + path += "/"; + loadProject(path); } } @@ -179,9 +93,7 @@ Item { selectFolder: false onAccepted: { var paths = openProjectFileDialog.fileUrls; - addProjectFiles(paths); + ProjectModelCode.doAddExistingFiles(paths); } } - - } diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js new file mode 100644 index 000000000..7bcfb5cd7 --- /dev/null +++ b/mix/qml/js/ProjectModel.js @@ -0,0 +1,142 @@ +/* + 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 . +*/ +/** @file ProjectModel.js + * @author Arkadiy Paronyan arkadiy@ethdev.com + * @date 2015 + * Ethereum IDE client. + */ + +function saveAll() { + saveProject(); + projectSaved(); +} + +function createProject() { + newProjectDialog.open(); +} + +function browseProject() { + openProjectFileDialog.open(); +} + +function closeProject() { + if (!isEmpty) { + if (haveUnsavedChanges) + saveMessageDialog.open(); + else + doCloseProject(); + } +} + +function saveProject() { + if (!isEmpty) { + var json = JSON.stringify(projectData); + var projectFile = projectPath + projectFileName; + fileIo.writeFile(projectFile, json); + } +} + +function loadProject(path) { + closeProject(); + console.log("loading project at " + path); + var projectFile = path + projectFileName; + var json = fileIo.readFile(projectFile); + projectData = JSON.parse(json); + if (!projectData.title) { + var parts = path.split("/"); + projectData.title = parts[parts.length - 2]; + } + projectPath = path; + if (!projectData.files) + projectData.files = []; + + for(var i = 0; i < projectData.files.length; i++) { + addFile(projectData.files[i]); + } + projectSettings.lastProjectPath = path; + projectLoaded(); +} + +function addExistingFile() { + addExistingFileDialog().open(); +} + +function addProjectFiles(files) { + for(var i = 0; i < files.length; i++) + addFile(files[i]); +} + +function addFile(fileName) { + var p = projectPath + fileName; + var fileData = { + contract: false, + path: p, + name: fileName, + documentId: fileName, + isText: true, + isContract: fileName.substring(fileName.length - 4, fileName.length) === ".sol", + }; + + projectListModel.append(fileData); +} + +function openDocument(documentId) { + for (var i = 0; i < projectListModel.count; i++) + if (projectListModel.get(i).documentId === documentId) + documentOpened(projectListModel.get(i)); +} + +function doCloseProject() { + console.log("closing project"); + projectListModel.clear(); + projectPath = ""; + projectData = null; + projectClosed(); +} + +function doCreateProject(title, path) { + closeProject(); + console.log("creating project " + title + " at " + path); + if (path[path.length - 1] !== "/") + path += "/"; + var dirPath = path + title + "/"; + fileIo.makeDir(dirPath); + var projectFile = dirPath + projectFileName; + + var indexFile = dirPath + "index.html"; + var contractsFile = dirPath + "contracts.sol"; + var projectData = { + title: title, + files: [ "contracts.sol", "index.html" ] + }; + + fileIo.writeFile(indexFile, ""); + fileIo.writeFile(contractsFile, "contract MyContract {}"); + var json = JSON.stringify(projectData); + fileIo.writeFile(projectFile, json); + loadProject(dirPath); +} + +function doAddExistingFiles(files) { + for(var i = 0; i < files.length; i++) { + var sourcePath = files[i]; + var sourceFileName = sourcePath.substring(sourcePath.lastIndexOf("/") + 1, sourcePath.length); + var destPath = projectPath + sourceFileName; + fileIo.copyFile(sourcePath, destPath); + addFile(sourceFileName); + } +} diff --git a/mix/qml/main.qml b/mix/qml/main.qml index c6cec939d..4bea535c2 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -21,14 +21,12 @@ ApplicationWindow { title: qsTr("File") MenuItem { action: createProjectAction } MenuItem { action: openProjectAction } + MenuItem { action: saveAllFilesAction } MenuItem { action: addExistingFileAction } MenuItem { action: addNewJsFileAction } MenuItem { action: addNewHtmlFileAction } MenuItem { action: addNewContractAction } - MenuItem { - text: qsTr("Exit") - onTriggered: Qt.quit(); - } + MenuItem { action: exitAppAction } } Menu { title: qsTr("Debug") @@ -54,6 +52,13 @@ ApplicationWindow { id: messageDialog } + Action { + id: exitAppAction + text: qsTr("Exit") + shortcut: "Ctrl+Q" + onTriggered: Qt.quit(); + } + Action { id: debugRunAction text: "&Run" @@ -116,4 +121,20 @@ ApplicationWindow { enabled: !ProjectModel.isEmpty onTriggered: ProjectModel.addExistingFile(); } + + Action { + id: saveAllFilesAction + text: qsTr("Save all") + shortcut: "Ctrl+S" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.saveAll(); + } + + Action { + id: closeProjectAction + text: qsTr("Close project") + shortcut: "Ctrl+W" + enabled: !ProjectModel.isEmpty + onTriggered: ProjectModel.closeProject(); + } }