Browse Source

- Warn user to reload file if file changed on disk.

- Ctrl-s: save the current doc only.
 - Ctrl-s: if contract: rerun, if document: reload preview only.
 - Ctrl-s: if compilation error do not rerun.
 - File change: add * and sync with codeeditor.js
cl-refactor
yann300 10 years ago
parent
commit
cb1dcb8b47
  1. 16
      mix/FileIo.cpp
  2. 11
      mix/FileIo.h
  3. 63
      mix/qml/CodeEditorView.qml
  4. 63
      mix/qml/FilesSection.qml
  5. 4
      mix/qml/MainContent.qml
  6. 14
      mix/qml/ProjectList.qml
  7. 25
      mix/qml/ProjectModel.qml
  8. 15
      mix/qml/TransactionLog.qml
  9. 14
      mix/qml/WebCodeEditor.qml
  10. 9
      mix/qml/WebPreview.qml
  11. 12
      mix/qml/html/codeeditor.js
  12. 15
      mix/qml/js/ProjectModel.js
  13. 2
      mix/qml/main.qml

16
mix/FileIo.cpp

@ -20,6 +20,7 @@
* Ethereum IDE client.
*/
#include <QFileSystemWatcher>
#include <QDebug>
#include <QDesktopServices>
#include <QMimeDatabase>
@ -40,7 +41,6 @@ using namespace dev;
using namespace dev::crypto;
using namespace dev::mix;
void FileIo::openFileBrowser(QString const& _dir)
{
QDesktopServices::openUrl(QUrl(_dir));
@ -87,7 +87,9 @@ QString FileIo::readFile(QString const& _url)
void FileIo::writeFile(QString const& _url, QString const& _data)
{
QFile file(pathFromUrl(_url));
QString path = pathFromUrl(_url);
m_watcher->removePath(path);
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream stream(&file);
@ -95,6 +97,7 @@ void FileIo::writeFile(QString const& _url, QString const& _data)
}
else
error(tr("Error writing file %1").arg(_url));
m_watcher->addPath(path);
}
void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl)
@ -191,3 +194,12 @@ QStringList FileIo::makePackage(QString const& _deploymentFolder)
return ret;
}
void FileIo::watchFileChanged(QString const& _path)
{
m_watcher->addPath(pathFromUrl(_path));
}
void FileIo::stopWatching(QString const& _path)
{
m_watcher->removePath(pathFromUrl(_path));
}

11
mix/FileIo.h

@ -22,6 +22,7 @@
#pragma once
#include <QFileSystemWatcher>
#include <libdevcore/CommonData.h>
#include <QObject>
@ -39,8 +40,11 @@ class FileIo: public QObject
signals:
/// Signalled in case of IO error
void error(QString const& _errorText);
/// Signnalled when a file is changed.
void fileChanged(QString const& _filePath);
public:
FileIo(): m_watcher(new QFileSystemWatcher(this)) { connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &FileIo::fileChanged); }
/// 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.
@ -55,12 +59,17 @@ public:
Q_INVOKABLE bool fileExists(QString const& _url);
/// Compress a folder, @returns sha3 of the compressed file.
Q_INVOKABLE QStringList makePackage(QString const& _deploymentFolder);
/// Open a file browser
/// Open a file browser.
Q_INVOKABLE void openFileBrowser(QString const& _dir);
/// Listen for files change in @arg _path.
Q_INVOKABLE void watchFileChanged(QString const& _path);
/// Stop Listenning for files change in @arg _path.
Q_INVOKABLE void stopWatching(QString const& _path);
private:
QString getHomePath() const;
QString pathFromUrl(QString const& _url);
QFileSystemWatcher* m_watcher;
};
}

63
mix/qml/CodeEditorView.qml

@ -2,12 +2,14 @@ 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: codeEditorView
property string currentDocumentId: ""
signal documentEdit(string documentId)
signal breakpointsChanged(string documentId)
signal isCleanChanged(var isClean, string documentId)
function getDocumentText(documentId) {
for (var i = 0; i < editorListModel.count; i++) {
@ -51,6 +53,9 @@ Item {
breakpointsChanged(document.documentId);
});
editor.setText(data, document.syntaxMode);
editor.onIsCleanChanged.connect(function(){
isCleanChanged(editor.isClean, document.documentId);
});
}
function getEditor(documentId) {
@ -91,6 +96,12 @@ Item {
editor.toggleBreakpoint();
}
function resetEditStatus() {
var editor = getEditor(currentDocumentId);
if (editor)
editor.changeGenerator();
}
Component.onCompleted: projectModel.codeEditor = codeEditorView;
Connections {
@ -100,8 +111,12 @@ Item {
}
onProjectSaving: {
for (var i = 0; i < editorListModel.count; i++)
{
fileIo.writeFile(editorListModel.get(i).path, editors.itemAt(i).item.getText());
resetEditStatus();
}
}
onProjectClosed: {
for (var i = 0; i < editorListModel.count; i++) {
editors.itemAt(i).visible = false;
@ -109,6 +124,31 @@ Item {
editorListModel.clear();
currentDocumentId = "";
}
onDocumentSaving: {
for (var i = 0; i < editorListModel.count; i++)
{
if (editorListModel.get(i).path === document)
{
fileIo.writeFile(document, editors.itemAt(i).item.getText());
resetEditStatus();
break;
}
}
}
}
MessageDialog
{
id: messageDialog
title: qsTr("File Changed")
text: qsTr("This file has been changed outside of the editor. Do you want to reload it?")
standardButtons: StandardButton.Yes | StandardButton.No
property variant item
property variant doc
onYes: {
doLoadDocument(item, doc);
}
}
Repeater {
@ -121,10 +161,18 @@ Item {
anchors.fill: parent
source: "CodeEditor.qml"
visible: (index >= 0 && index < editorListModel.count && currentDocumentId === editorListModel.get(index).documentId)
property bool changed: false
onVisibleChanged: {
loadIfNotLoaded()
if (visible && item)
loader.item.setFocus();
if (visible && changed)
{
changed = false;
messageDialog.item = loader.item;
messageDialog.doc = editorListModel.get(index);
messageDialog.open();
}
}
Component.onCompleted: {
loadIfNotLoaded()
@ -133,6 +181,21 @@ Item {
doLoadDocument(loader.item, editorListModel.get(index))
}
Connections
{
target: projectModel
onDocumentChanged: {
if (currentDocumentId == documentId)
{
messageDialog.item = loader.item;
messageDialog.doc = editorListModel.get(index);
messageDialog.open();
}
else
changed = true;
}
}
function loadIfNotLoaded () {
if(visible && !active) {
active = true;

63
mix/qml/FilesSection.qml

@ -141,34 +141,53 @@ Rectangle
color: isSelected ? ProjectFilesStyle.documentsList.highlightColor : "transparent"
property bool isSelected
property bool renameMode
Text {
id: nameText
height: parent.height
visible: !renameMode
color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color
text: name;
font.family: fileNameFont.name
font.pointSize: ProjectFilesStyle.documentsList.fontSize
Row {
spacing: 3
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
anchors.left: parent.left
anchors.leftMargin: ProjectFilesStyle.general.leftMargin + 2
width: parent.width
Connections
{
target: selManager
onSelected: {
if (groupName != sectionName)
rootItem.isSelected = false;
else if (doc === documentId)
rootItem.isSelected = true;
else
rootItem.isSelected = false;
Text {
id: nameText
height: parent.height
visible: !renameMode
color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color
text: name;
font.family: fileNameFont.name
font.pointSize: ProjectFilesStyle.documentsList.fontSize
verticalAlignment: Text.AlignVCenter
Connections
{
target: selManager
onSelected: {
if (groupName != sectionName)
rootItem.isSelected = false;
else if (doc === documentId)
rootItem.isSelected = true;
else
rootItem.isSelected = false;
if (rootItem.isSelected && section.state === "hidden")
section.state = "";
if (rootItem.isSelected && section.state === "hidden")
section.state = "";
}
onIsCleanChanged: {
if (groupName === sectionName && doc === documentId)
editStatusLabel.visible = !isClean;
}
}
}
DefaultLabel {
id: editStatusLabel
visible: false
color: rootItem.isSelected ? ProjectFilesStyle.documentsList.selectedColor : ProjectFilesStyle.documentsList.color
verticalAlignment: Text.AlignVCenter
text: "*"
width: 10
height: parent.height
}
}
TextInput {

4
mix/qml/MainContent.qml

@ -179,6 +179,10 @@ Rectangle {
width: 350
Layout.minimumWidth: 250
Layout.fillHeight: true
Connections {
target: projectModel.codeEditor
}
}
Rectangle {
id: contentView

14
mix/qml/ProjectList.qml

@ -67,8 +67,6 @@ Item {
color: ProjectFilesStyle.documentsList.background
}
Rectangle
{
Layout.fillWidth: true
@ -83,6 +81,7 @@ Item {
Repeater {
model: [qsTr("Contracts"), qsTr("Javascript"), qsTr("Web Pages"), qsTr("Styles"), qsTr("Images"), qsTr("Misc")];
signal selected(string doc, string groupName)
signal isCleanChanged(string doc, string groupName, var isClean)
property int incr: -1;
id: sectionRepeater
FilesSection
@ -145,6 +144,17 @@ Item {
}
}
onIsCleanChanged: {
for (var si = 0; si < sectionModel.count; si++) {
var document = sectionModel.get(si);
if (documentId === document.documentId && document.groupName === modelData)
{
selManager.isCleanChanged(documentId, modelData, isClean);
break;
}
}
}
onDocumentOpened: {
if (document.groupName === modelData)
sectionRepeater.selected(document.documentId, modelData);

25
mix/qml/ProjectModel.qml

@ -7,12 +7,13 @@ import Qt.labs.settings 1.0
import "js/ProjectModel.js" as ProjectModelCode
Item {
id: projectModel
signal projectClosed
signal projectLoading(var projectData)
signal projectLoaded()
signal documentSaving(var document)
signal documentChanged(var document)
signal documentOpened(var document)
signal documentRemoved(var documentId)
signal documentUpdated(var documentId) //renamed
@ -21,10 +22,12 @@ Item {
signal projectSaved()
signal newProject(var projectData)
signal documentSaved(var documentId)
signal contractSaved(var documentId)
signal deploymentStarted()
signal deploymentStepChanged(string message)
signal deploymentComplete()
signal deploymentError(string error)
signal isCleanChanged(var isClean, string documentId)
property bool isEmpty: (projectPath === "")
readonly property string projectFileName: ".mix"
@ -41,6 +44,7 @@ Item {
//interface
function saveAll() { ProjectModelCode.saveAll(); }
function saveCurrentDocument() { ProjectModelCode.saveCurrentDocument(); }
function createProject() { ProjectModelCode.createProject(); }
function closeProject() { ProjectModelCode.closeProject(); }
function saveProject() { ProjectModelCode.saveProject(); }
@ -69,6 +73,13 @@ Item {
}
}
Connections {
target: codeEditor
onIsCleanChanged: {
isCleanChanged(isClean, documentId);
}
}
NewProjectDialog {
id: newProjectDialog
visible: false
@ -79,6 +90,18 @@ Item {
}
}
Connections
{
target: fileIo
property bool saving: false
onFileChanged:
{
fileIo.watchFileChanged(_filePath);
if (_filePath.indexOf(currentDocumentId, _filePath.length - currentDocumentId.length))
documentChanged(_filePath);
}
}
MessageDialog {
id: saveMessageDialog
title: qsTr("Project")

15
mix/qml/TransactionLog.qml

@ -30,12 +30,21 @@ Item {
anchors.fill: parent
RowLayout {
Connections
{
id: compilationStatus
target: codeModel
property bool compilationComplete: false
onCompilationComplete: compilationComplete = true
onCompilationError: compilationComplete = false
}
Connections
{
target: projectModel
onProjectSaved:
{
if (codeModel.hasContract && !clientModel.running)
if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running)
projectModel.stateListModel.debugDefaultState();
}
onProjectClosed:
@ -44,6 +53,10 @@ Item {
transactionModel.clear();
callModel.clear();
}
onContractSaved: {
if (compilationStatus.compilationComplete && codeModel.hasContract && !clientModel.running)
projectModel.stateListModel.debugDefaultState();
}
}
ComboBox {

14
mix/qml/WebCodeEditor.qml

@ -6,8 +6,9 @@ import QtWebEngine 1.0
import QtWebEngine.experimental 1.0
Item {
signal editorTextChanged;
signal breakpointsChanged;
signal editorTextChanged
signal breakpointsChanged
property bool isClean: true
property string currentText: ""
property string currentMode: ""
property bool initialized: false
@ -50,6 +51,10 @@ Item {
editorBrowser.runJavaScript("toggleBreakpoint()");
}
function changeGenerator() {
editorBrowser.runJavaScript("changeGeneration()", function(result) {});
}
Connections {
target: appContext
onClipboardChanged: syncClipboard()
@ -75,6 +80,7 @@ Item {
runJavaScript("getTextChanged()", function(result) { });
pollTimer.running = true;
syncClipboard();
parent.changeGenerator();
}
}
@ -103,7 +109,9 @@ Item {
});
}
});
editorBrowser.runJavaScript("isClean()", function(result) {
isClean = result;
});
}
}
}

9
mix/qml/WebPreview.qml

@ -86,8 +86,7 @@ Item {
Connections {
target: projectModel
//onProjectSaved : reloadOnSave();
//onDocumentSaved: reloadOnSave();
onDocumentAdded: {
var document = projectModel.getDocument(documentId)
if (document.isHtml)
@ -115,6 +114,12 @@ Item {
}
}
onDocumentSaved:
{
if (!projectModel.getDocument(documentId).isContract)
reloadOnSave();
}
onProjectClosed: {
pageListModel.clear();
}

12
mix/qml/html/codeeditor.js

@ -18,7 +18,6 @@ editor.breakpointsChangeRegistered = false;
editor.on("change", function(eMirror, object) {
editor.changeRegistered = true;
});
var mac = /Mac/.test(navigator.platform);
@ -110,3 +109,14 @@ highlightExecution = function(start, end) {
executionMark.clear();
executionMark = editor.markText(editor.posFromIndex(start), editor.posFromIndex(end), { className: "CodeMirror-exechighlight" });
}
var changeId;
changeGeneration = function()
{
changeId = editor.changeGeneration(true);
}
isClean = function()
{
return editor.isClean(changeId);
}

15
mix/qml/js/ProjectModel.js

@ -25,6 +25,15 @@ Qt.include("TransactionHelper.js")
var htmlTemplate = "<html>\n<head>\n<script>\n</script>\n</head>\n<body>\n<script>\n</script>\n</body>\n</html>";
var contractTemplate = "contract Contract {\n}\n";
function saveCurrentDocument()
{
documentSaving(projectPath + currentDocumentId);
var doc = projectListModel.get(getDocumentIndex(currentDocumentId));
if (doc.isContract)
contractSaved(currentDocumentId);
documentSaved(currentDocumentId);
}
function saveAll() {
saveProject();
}
@ -55,7 +64,7 @@ function saveProject() {
deploymentDir: projectModel.deploymentDir
};
for (var i = 0; i < projectListModel.count; i++)
projectData.files.push(projectListModel.get(i).fileName)
projectData.files.push(projectListModel.get(i).fileName);
projectSaving(projectData);
var json = JSON.stringify(projectData, null, "\t");
var projectFile = projectPath + projectFileName;
@ -105,7 +114,6 @@ function loadProject(path) {
contractSources[doc.documentId] = fileIo.readFile(doc.path);
}
codeModel.reset(contractSources);
}
function addFile(fileName) {
@ -132,6 +140,7 @@ function addFile(fileName) {
};
projectListModel.append(docData);
fileIo.watchFileChanged(p);
return docData.documentId;
}
@ -184,6 +193,7 @@ function doCloseProject() {
projectListModel.clear();
projectPath = "";
currentDocumentId = "";
fileIo.stopFilesWatcher();
projectClosed();
}
@ -247,6 +257,7 @@ function removeDocument(documentId) {
var document = projectListModel.get(i);
if (!document.isContract) {
projectListModel.remove(i);
fileIo.stopWatching(projectPath+ documentId);
documentRemoved(documentId);
}
}

2
mix/qml/main.qml

@ -281,7 +281,7 @@ ApplicationWindow {
text: qsTr("Save All")
shortcut: "Ctrl+S"
enabled: !projectModel.isEmpty
onTriggered: projectModel.saveAll();
onTriggered: projectModel.saveCurrentDocument();
}
Action {

Loading…
Cancel
Save