Browse Source

continue project model implementation

cl-refactor
arkpar 10 years ago
parent
commit
715e0a4745
  1. 32
      mix/CodeEditorExtensionManager.cpp
  2. 5
      mix/CodeEditorExtensionManager.h
  3. 29
      mix/FileIo.cpp
  4. 20
      mix/FileIo.h
  5. 1
      mix/MixApplication.cpp
  6. 3
      mix/qml.qrc
  7. 81
      mix/qml/CodeEditor.qml
  8. 13
      mix/qml/CodeEditorModel.qml
  9. 80
      mix/qml/CodeEditorView.qml
  10. 72
      mix/qml/MainContent.qml
  11. 12
      mix/qml/NewProjectDialog.qml
  12. 28
      mix/qml/ProjectList.qml
  13. 123
      mix/qml/ProjectModel.qml
  14. 2
      mix/qml/StateList.qml
  15. 53
      mix/qml/main.qml

32
mix/CodeEditorExtensionManager.cpp

@ -51,15 +51,6 @@ void CodeEditorExtensionManager::loadEditor(QQuickItem* _editor)
if (!_editor)
return;
QVariant doc = _editor->property("textDocument");
if (doc.canConvert<QQuickTextDocument*>())
{
QQuickTextDocument* qqdoc = doc.value<QQuickTextDocument*>();
if (qqdoc)
{
m_doc = qqdoc->textDocument();
}
}
}
void CodeEditorExtensionManager::initExtensions()
@ -67,7 +58,6 @@ void CodeEditorExtensionManager::initExtensions()
std::shared_ptr<ConstantCompilationControl> output = std::make_shared<ConstantCompilationControl>(m_appContext);
std::shared_ptr<AssemblyDebuggerControl> debug = std::make_shared<AssemblyDebuggerControl>(m_appContext);
std::shared_ptr<StateListView> stateList = std::make_shared<StateListView>(m_appContext);
QObject::connect(m_doc, &QTextDocument::contentsChange, this, &CodeEditorExtensionManager::onCodeChange);
QObject::connect(debug.get(), &AssemblyDebuggerControl::runFailed, output.get(), &ConstantCompilationControl::displayError);
QObject::connect(m_appContext->codeModel(), &CodeModel::compilationComplete, this, &CodeEditorExtensionManager::applyCodeHighlight);
@ -97,35 +87,21 @@ void CodeEditorExtensionManager::initExtension(std::shared_ptr<Extension> _ext)
m_features.append(_ext);
}
void CodeEditorExtensionManager::setEditor(QQuickItem* _editor)
{
this->loadEditor(_editor);
this->initExtensions();
auto args = QApplication::arguments();
if (args.length() > 1)
{
QString path = args[1];
QFile file(path);
if (file.exists() && file.open(QFile::ReadOnly))
m_doc->setPlainText(file.readAll());
}
}
void CodeEditorExtensionManager::onCodeChange()
{
m_appContext->codeModel()->updateFormatting(m_doc); //update old formatting
m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText());
// m_appContext->codeModel()->updateFormatting(m_doc); //update old formatting
// m_appContext->codeModel()->registerCodeChange(m_doc->toPlainText());
}
void CodeEditorExtensionManager::applyCodeHighlight()
{
m_appContext->codeModel()->updateFormatting(m_doc);
// m_appContext->codeModel()->updateFormatting(m_doc);
}
void CodeEditorExtensionManager::setRightTabView(QQuickItem* _tabView)
{
m_rightTabView = _tabView;
initExtensions(); //TODO: this is not the right place for it
}
void CodeEditorExtensionManager::setTabView(QQuickItem* _tabView)

5
mix/CodeEditorExtensionManager.h

@ -43,7 +43,6 @@ class CodeEditorExtensionManager: public QObject
{
Q_OBJECT
Q_PROPERTY(QQuickItem* editor MEMBER m_editor WRITE setEditor)
Q_PROPERTY(QQuickItem* tabView MEMBER m_tabView WRITE setTabView)
Q_PROPERTY(QQuickItem* rightTabView MEMBER m_rightTabView WRITE setRightTabView)
@ -54,8 +53,6 @@ public:
void initExtensions();
/// Initialize extension.
void initExtension(std::shared_ptr<Extension>);
/// Set current text editor.
void setEditor(QQuickItem*);
/// Set current tab view
void setTabView(QQuickItem*);
/// Set current right tab view.
@ -66,11 +63,9 @@ private slots:
void applyCodeHighlight();
private:
QQuickItem* m_editor;
QVector<std::shared_ptr<Extension>> m_features;
QQuickItem* m_tabView;
QQuickItem* m_rightTabView;
QTextDocument* m_doc;
AppContext* m_appContext;
void loadEditor(QQuickItem* _editor);
};

29
mix/FileIo.cpp

@ -25,20 +25,23 @@
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QUrl>
#include "FileIo.h"
using namespace dev::mix;
void FileIo::makeDir(QString const& _path)
void FileIo::makeDir(QString const& _url)
{
QDir dirPath(_path);
QUrl url(_url);
QDir dirPath(url.path());
if (!dirPath.exists())
dirPath.mkpath(dirPath.path());
}
QString FileIo::readFile(QString const& _path)
QString FileIo::readFile(QString const& _url)
{
QFile file(_path);
QUrl url(_url);
QFile file(url.path());
if(file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream stream(&file);
@ -46,23 +49,27 @@ QString FileIo::readFile(QString const& _path)
return data;
}
else
throw std::runtime_error(tr("Error reading file %1").arg(_path).toStdString());
error(tr("Error reading file %1").arg(_url));
return QString();
}
void FileIo::writeFile(QString const& _path, QString const& _data)
void FileIo::writeFile(QString const& _url, QString const& _data)
{
QFile file(_path);
QUrl url(_url);
QFile file(url.path());
if(file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream stream(&file);
stream << _data;
}
else
throw std::runtime_error(tr("Error writing file %1").arg(_path).toStdString());
error(tr("Error writing file %1").arg(_url));
}
void FileIo::copyFile(QString const& _sourcePath, QString const& _destPath)
void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl)
{
if (!QFile::copy(_sourcePath, _destPath))
throw std::runtime_error(tr("Error copying file %1 to %2").arg(_sourcePath).arg(_destPath).toStdString());
QUrl sourceUrl(_sourceUrl);
QUrl destUrl(_destUrl);
if (!QFile::copy(sourceUrl.path(), destUrl.path()))
error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl));
}

20
mix/FileIo.h

@ -34,15 +34,19 @@ class FileIo : public QObject
{
Q_OBJECT
signals:
/// Signalled in case of IO error
void error(QString const& _errorText);
public:
/// Create a directory if it does not exist. Throws on failure.
Q_INVOKABLE void makeDir(QString const& _path);
/// Read file contents to a string. Throws on failure.
Q_INVOKABLE QString readFile(QString const& _path);
/// Write contents to a file. Throws on failure.
Q_INVOKABLE void writeFile(QString const& _path, QString const& _data);
/// Copy a file from _sourcePath to _destPath. Throws on failure.
Q_INVOKABLE void copyFile(QString const& _sourcePath, QString const& _destPath);
/// Create a directory if it does not exist. Signals on failure.
Q_INVOKABLE void makeDir(QString const& _url);
/// Read file contents to a string. Signals on failure.
Q_INVOKABLE QString readFile(QString const& _url);
/// Write contents to a file. Signals on failure.
Q_INVOKABLE void writeFile(QString const& _url, QString const& _data);
/// Copy a file from _sourcePath to _destPath. Signals on failure.
Q_INVOKABLE void copyFile(QString const& _sourceUrl, QString const& _destUrl);
};
}

1
mix/MixApplication.cpp

@ -35,6 +35,7 @@ MixApplication::MixApplication(int _argc, char* _argv[]):
qmlRegisterType<CodeEditorExtensionManager>("CodeEditorExtensionManager", 1, 0, "CodeEditorExtensionManager");
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();
}

3
mix/qml.qrc

@ -15,5 +15,8 @@
<file>qml/js/Debugger.js</file>
<file>qml/NewProjectDialog.qml</file>
<file>qml/ProjectModel.qml</file>
<file>qml/CodeEditorModel.qml</file>
<file>qml/CodeEditor.qml</file>
<file>qml/CodeEditorView.qml</file>
</qresource>
</RCC>

81
mix/qml/CodeEditor.qml

@ -0,0 +1,81 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import QtQuick.Controls.Styles 1.1
Component {
Item {
signal editorTextChanged
function setText(text) {
codeEditor.text = text;
}
function getText() {
return codeEditor.text;
}
anchors.fill: parent
id: contentView
width: parent.width
height: parent.height * 0.7
Rectangle {
id: lineColumn
property int rowHeight: codeEditor.font.pixelSize + 3
color: "#202020"
width: 50
height: parent.height
Column {
y: -codeEditor.flickableItem.contentY + 4
width: parent.width
Repeater {
model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight))
delegate: Text {
id: text
color: codeEditor.textColor
font: codeEditor.font
width: lineColumn.width - 4
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
height: lineColumn.rowHeight
renderType: Text.NativeRendering
text: index + 1
}
}
}
}
TextArea {
id: codeEditor
textColor: "#EEE8D5"
style: TextAreaStyle {
backgroundColor: "#002B36"
}
anchors.left: lineColumn.right
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
wrapMode: TextEdit.NoWrap
frameVisible: false
height: parent.height
font.family: "Monospace"
font.pointSize: 12
width: parent.width
tabChangesFocus: false
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
onTextChanged: {
editorTextChanged();
}
}
}
}

13
mix/qml/CodeEditorModel.qml

@ -0,0 +1,13 @@
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: []
}

80
mix/qml/CodeEditorView.qml

@ -0,0 +1,80 @@
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
import org.ethereum.qml.ProjectModel 1.0
Item {
property string currentDocumentId: ""
function getDocumentText(documentId) {
for (i = 0; i < editorListModel.count; i++) {
if (editorListModel.get(i).documentId === documentId) {
return editors.itemAt(i).getDocumentText();
}
}
return "";
}
function openDocument(document) {
loadDocument(document);
currentDocumentId = document.documentId;
}
function loadDocument(document) {
for (var i = 0; i < editorListModel.count; i++)
if (editorListModel.get(i).documentId === document.documentId)
return; //already open
editorListModel.append(document);
}
function doLoadDocument(editor, document) {
var data = fileIo.readFile(document.path);
if (document.isContract)
editor.onEditorTextChanged.connect(function() {
codeModel.registerCodeChange(editor.getText());
});
editor.setText(data);
}
Connections {
target: ProjectModel
onDocumentOpen: {
openDocument(document);
}
}
CodeEditor {
id: codeEditor
}
Repeater {
id: editors
model: editorListModel
delegate: Loader {
active: false;
asynchronous: true
anchors.fill: parent
sourceComponent: codeEditor
visible: (currentDocumentId === editorListModel.get(index).documentId)
onVisibleChanged: {
loadIfNotLoaded()
}
Component.onCompleted: {
loadIfNotLoaded()
}
onLoaded: { doLoadDocument(item, editorListModel.get(index)) }
function loadIfNotLoaded () {
if(visible && !active) {
active = true;
}
}
}
}
ListModel {
id: editorListModel
}
}

72
mix/qml/MainContent.qml

@ -22,80 +22,21 @@ Rectangle {
anchors.fill: parent
ProjectList {
anchors.left: parent.left
width: parent.width * 0.2
height: parent.height
Layout.minimumWidth: 20
Layout.minimumWidth: 200
}
SplitView {
//anchors.fill: parent
width: parent.width * 0.6
orientation: Qt.Vertical
Rectangle {
anchors.top: parent.top
id: contentView
width: parent.width
height: parent.height * 0.7
Item {
anchors.fill: parent
Rectangle {
id: lineColumn
property int rowHeight: codeEditor.font.pixelSize + 3
color: "#202020"
width: 50
height: parent.height
Column {
y: -codeEditor.flickableItem.contentY + 4
width: parent.width
Repeater {
model: Math.max(codeEditor.lineCount + 2, (lineColumn.height/lineColumn.rowHeight))
delegate: Text {
id: text
color: codeEditor.textColor
font: codeEditor.font
width: lineColumn.width - 4
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
height: lineColumn.rowHeight
renderType: Text.NativeRendering
text: index + 1
}
}
}
}
TextArea {
id: codeEditor
textColor: "#EEE8D5"
style: TextAreaStyle {
backgroundColor: "#002B36"
}
CodeEditorView {
height: parent.height * 0.7
anchors.top: parent.top
width: parent.width
}
anchors.left: lineColumn.right
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
wrapMode: TextEdit.NoWrap
frameVisible: false
height: parent.height
font.family: "Monospace"
font.pointSize: 12
width: parent.width
//anchors.centerIn: parent
tabChangesFocus: false
Keys.onPressed: {
if (event.key === Qt.Key_Tab) {
codeEditor.insert(codeEditor.cursorPosition, "\t");
event.accepted = true;
}
}
}
}
}
Rectangle {
anchors.bottom: parent.bottom
id: contextualView
@ -126,7 +67,6 @@ Rectangle {
CodeEditorExtensionManager {
tabView: contextualTabs
rightTabView: rightPaneTabs
editor: codeEditor
}
}
}

12
mix/qml/NewProjectDialog.qml

@ -14,7 +14,7 @@ Window {
visible: false
property alias projectTitle : titleField.text
property alias projectPath : pathField.text
readonly property string projectPath : "file://" + pathField.text
signal accepted
function open() {
@ -83,10 +83,10 @@ Window {
title: qsTr("Please choose a path for the project")
selectFolder: true
onAccepted: {
var u = createProjectFileDialog.fileUrl.toString();
if (u.indexOf("file://") == 0)
u = u.substring(7, u.length)
pathField.text = u;
}
var u = createProjectFileDialog.fileUrl.toString();
if (u.indexOf("file://") == 0)
u = u.substring(7, u.length)
pathField.text = u;
}
}
}

28
mix/qml/ProjectList.qml

@ -6,8 +6,16 @@ import org.ethereum.qml.ProjectModel 1.0
Item {
ListView {
id: projectList
model: ProjectModel.listModel
anchors.fill: parent
delegate: renderDelegate
highlight: Rectangle {
color: "lightsteelblue";
}
highlightFollowsCurrentItem: true
focus: true
clip: true
}
Component {
@ -21,19 +29,23 @@ Item {
Text {
Layout.fillWidth: true
Layout.fillHeight: true
text: title
text: name
font.pointSize: 12
verticalAlignment: Text.AlignBottom
}
}
MouseArea {
id: mouseArea
z: 1
hoverEnabled: false
anchors.fill: parent
onClicked:{
projectList.currentIndex = index;
ProjectModel.documentOpen(ProjectModel.listModel.get(index));
}
}
}
}
Action {
id: createProjectAction
text: qsTr("&New project")
shortcut: "Ctrl+N"
enabled: true;
onTriggered: ProjectModel.createProject();
}
}

123
mix/qml/ProjectModel.qml

@ -12,8 +12,9 @@ Item {
signal projectClosed
signal projectLoaded
signal documentOpen(var document)
property bool isEmpty: projectFile === ""
property bool isEmpty: (projectFile === "")
readonly property string projectFileName: ".mix"
property bool haveUnsavedChanges: false
@ -30,24 +31,29 @@ Item {
newProjectDialog.open();
}
function browseProject() {
openProjectFileDialog.open();
}
function closeProject() {
console.log("closing project");
if (haveUnsavedChanges)
saveMessageDialog.open();
else
doCloseProject();
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)
fileIo.writeFile(projectFile, json);
}
}
function loadProject(path) {
if (!isEmpty)
closeProject();
closeProject();
console.log("loading project at " + path);
var json = fileIo.readFile(path);
projectData = JSON.parse(json);
@ -55,14 +61,35 @@ Item {
if (!projectData.files)
projectData.files = [];
for(var i = 0; i < projectData.files; i++) {
for(var i = 0; i < projectData.files.length; i++) {
var p = projectData.files[i];
projectListModel.append({
path: p,
name: p.substring(p.lastIndexOf("/") + 1, p.length)
});
addFile(p);
}
onProjectLoaded();
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() {
@ -73,25 +100,39 @@ Item {
}
function doCreateProject(title, path) {
if (!isEmpty)
closeProject();
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;
fileIo.writeFile(projectFile, "");
var indexFile = dirPath + "/index.html";
var contractsFile = dirPath + "/contracts.sol";
var projectData = {
files: [ indexFile, contractsFile ]
};
fileIo.writeFile(indexFile, "<html></html>");
fileIo.writeFile(contractsFile, "contract MyContract {}");
var json = JSON.stringify(projectData);
fileIo.writeFile(projectFile, json);
loadProject(projectFile);
}
Component.onCompleted: {
if (projectSettings.lastProjectPath)
loadProject(projectSettings.lastProjectPath)
}
NewProjectDialog {
id: newProjectDialog
visible: false
onAccepted: {
var title = newProjectDialog.projectTitle;
var path = newProjectDialog.projectPath;
projectModel.doCreateProject(title, path);
doCreateProject(title, path);
}
}
@ -114,27 +155,33 @@ Item {
id: projectListModel
}
Component {
id: renderDelegate
Item {
id: wrapperItem
height: 20
width: parent.width
RowLayout {
anchors.fill: parent
Text {
Layout.fillWidth: true
Layout.fillHeight: true
text: title
font.pointSize: 12
verticalAlignment: Text.AlignBottom
}
}
}
}
Settings {
id: projectSettings
property string lastProjectPath;
}
FileDialog {
id: openProjectFileDialog
visible: false
title: qsTr("Open a project")
selectFolder: true
onAccepted: {
var path = openProjectFileDialog.fileUrl.toString();
path += "/" + projectFileName;
loadProject(path);
}
}
FileDialog {
id: addExistingFileDialog
visible: false
title: qsTr("Add a file")
selectFolder: false
onAccepted: {
var paths = openProjectFileDialog.fileUrls;
addProjectFiles(paths);
}
}
}

2
mix/qml/StateList.qml

@ -21,6 +21,8 @@ Rectangle {
stateListModel.clear();
}
onProjectLoaded: {
if (!target.projectData.states)
target.projectData.states = [];
var items = target.projectData.states;
for(var i = 0; i < items.length; i++) {
stateListModel.append(items[i]);

53
mix/qml/main.qml

@ -5,6 +5,7 @@ import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import QtQuick.Window 2.1
import CodeEditorExtensionManager 1.0
import org.ethereum.qml.ProjectModel 1.0
ApplicationWindow {
id: mainApplication
@ -18,6 +19,12 @@ ApplicationWindow {
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem { action: createProjectAction }
MenuItem { action: openProjectAction }
MenuItem { action: addExistingFileAction }
MenuItem { action: addNewJsFileAction }
MenuItem { action: addNewHtmlFileAction }
MenuItem { action: addNewContractAction }
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
@ -62,5 +69,51 @@ ApplicationWindow {
onTriggered: debugModel.resetState();
}
Action {
id: createProjectAction
text: qsTr("&New project")
shortcut: "Ctrl+N"
enabled: true;
onTriggered: ProjectModel.createProject();
}
Action {
id: openProjectAction
text: qsTr("&Open project")
shortcut: "Ctrl+O"
enabled: true;
onTriggered: ProjectModel.browseProject();
}
Action {
id: addNewJsFileAction
text: qsTr("New JavaScript file")
shortcut: "Ctrl+Alt+J"
enabled: !ProjectModel.isEmpty
onTriggered: ProjectModel.addJsFile();
}
Action {
id: addNewHtmlFileAction
text: qsTr("New HTML file")
shortcut: "Ctrl+Alt+H"
enabled: !ProjectModel.isEmpty
onTriggered: ProjectModel.addHtmlFile();
}
Action {
id: addNewContractAction
text: qsTr("New contract")
shortcut: "Ctrl+Alt+C"
enabled: !ProjectModel.isEmpty
onTriggered: ProjectModel.addContract();
}
Action {
id: addExistingFileAction
text: qsTr("Add existing file")
shortcut: "Ctrl+Alt+A"
enabled: !ProjectModel.isEmpty
onTriggered: ProjectModel.addExistingFile();
}
}

Loading…
Cancel
Save