diff --git a/mix/qml/FilesSection.qml b/mix/qml/FilesSection.qml new file mode 100644 index 000000000..d532fb2b1 --- /dev/null +++ b/mix/qml/FilesSection.qml @@ -0,0 +1,243 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.3 +import "." + + +ColumnLayout { + id: wrapperItem + signal documentSelected(string doc, string groupName) + property alias model: filesList.model + property string sectionName; + property variant selManager; + Layout.fillWidth: true + Layout.minimumHeight: hiddenHeightTopLevel() + height: hiddenHeightTopLevel() + Layout.maximumHeight: hiddenHeightTopLevel() + spacing: 0 + + function hiddenHeightTopLevel() + { + return section.state === "hidden" ? Style.documentsList.height : Style.documentsList.fileNameHeight * model.count + Style.documentsList.height; + } + + function hiddenHeightRepeater() + { + return section.state === "hidden" ? 0 : Style.documentsList.fileNameHeight * wrapperItem.model.count; + } + + function hiddenHeightElement() + { + return section.state === "hidden" ? 0 : Style.documentsList.fileNameHeight; + } + + function getDocumentIndex(documentId) + { + for (var i = 0; i < model.count; i++) + if (model.get(i).documentId === documentId) + return i; + return -1; + } + + function removeDocument(documentId) + { + var i = getDocumentIndex(documentId); + if (i !== -1) + model.remove(i); + } + + FontLoader + { + id: fileNameFont + source: "qrc:/qml/fonts/SourceSansPro-Regular.ttf" + } + + RowLayout + { + anchors.top: parent.top + id: rowCol + width: parent.width + height: Style.documentsList.height + + Image { + source: "qrc:/qml/img/opentriangleindicator_filesproject.png" + width: 15 + sourceSize.width: 15 + id: imgArrow + anchors.right: section.left + anchors.rightMargin: 5 + anchors.top: parent.top + anchors.topMargin: 8 + } + + Text + { + id: section + text: sectionName + anchors.left: parent.left + anchors.leftMargin: Style.general.leftMargin + color: Style.documentsList.sectionColor + font.family: fileNameFont.name + font.pointSize: Style.documentsList.fontSize + font.weight: Font.Bold + font.letterSpacing: 1 + states: [ + State { + name: "hidden" + PropertyChanges { target: filesList; visible: false; } + PropertyChanges { target: rowCol; Layout.minimumHeight: Style.documentsList.height; Layout.maximumHeight: Style.documentsList.height; height: Style.documentsList.height; } + PropertyChanges { target: imgArrow; source: "qrc:/qml/img/closedtriangleindicator_filesproject.png" } + } + ] + } + + MouseArea { + id: titleMouseArea + anchors.fill: parent + hoverEnabled: true + z: 2 + onClicked: { + if (section.state === "hidden") + section.state = ""; + else + section.state = "hidden"; + } + } + } + + ColumnLayout { + height: wrapperItem.hiddenHeightRepeater() + Layout.minimumHeight: wrapperItem.hiddenHeightRepeater() + Layout.preferredHeight: wrapperItem.hiddenHeightRepeater() + Layout.maximumHeight: wrapperItem.hiddenHeightRepeater() + width: parent.width + visible: section.state !== "hidden" + spacing: 0 + Repeater + { + id: filesList + visible: section.state !== "hidden" + Rectangle + { + visible: section.state !== "hidden" + id: rootItem + Layout.fillWidth: true + Layout.minimumHeight: wrapperItem.hiddenHeightElement() + Layout.preferredHeight: wrapperItem.hiddenHeightElement() + Layout.maximumHeight: wrapperItem.hiddenHeightElement() + height: wrapperItem.hiddenHeightElement() + color: isSelected ? Style.documentsList.highlightColor : Style.documentsList.background + property bool isSelected + property bool renameMode + Text { + id: nameText + height: parent.height + visible: !renameMode + color: rootItem.isSelected ? Style.documentsList.selectedColor : Style.documentsList.color + text: name; + font.family: fileNameFont.name + font.pointSize: Style.documentsList.fontSize + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + anchors.left: parent.left + anchors.leftMargin: Style.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; + } + } + } + + TextInput { + id: textInput + text: nameText.text + visible: renameMode + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: Style.general.leftMargin + MouseArea { + id: textMouseArea + anchors.fill: parent + hoverEnabled: true + z: 2 + onClicked: { + textInput.forceActiveFocus(); + } + } + + onVisibleChanged: { + if (visible) { + selectAll(); + forceActiveFocus(); + } + } + + onAccepted: close(true); + onCursorVisibleChanged: { + if (!cursorVisible) + close(false); + } + onFocusChanged: { + if (!focus) + close(false); + } + function close(accept) { + rootItem.renameMode = false; + if (accept) + { + var i = getDocumentIndex(documentId); + projectModel.renameDocument(documentId, textInput.text); + wrapperItem.model.set(i, projectModel.getDocument(documentId)); + } + } + } + + MouseArea { + id: mouseArea + z: 1 + hoverEnabled: false + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked:{ + if (mouse.button === Qt.RightButton && !isContract) + contextMenu.popup(); + else if (mouse.button === Qt.LeftButton) + { + rootItem.isSelected = true; + projectModel.openDocument(documentId); + documentSelected(documentId, groupName); + } + } + } + + Menu { + id: contextMenu + MenuItem { + text: qsTr("Rename") + onTriggered: { + rootItem.renameMode = true; + } + } + MenuItem { + text: qsTr("Delete") + onTriggered: { + projectModel.removeDocument(documentId); + wrapperItem.removeDocument(documentId); + } + } + } + } + } + } +} + diff --git a/mix/qml/MainContent.qml b/mix/qml/MainContent.qml index e6c636514..4fa1dfe52 100644 --- a/mix/qml/MainContent.qml +++ b/mix/qml/MainContent.qml @@ -7,6 +7,7 @@ import Qt.labs.settings 1.0 import org.ethereum.qml.QEther 1.0 import "js/QEtherHelper.js" as QEtherHelper import "js/TransactionHelper.js" as TransactionHelper +import "." Rectangle { @@ -121,6 +122,12 @@ Rectangle { } } + Rectangle{ + Layout.fillWidth: true + height: 1 + color: "#8c8c8c" + } + Rectangle { Layout.fillWidth: true Layout.preferredHeight: root.height - headerView.height; @@ -136,16 +143,16 @@ Rectangle { { anchors.fill: parent handleDelegate: Rectangle { - width: 4 - height: 4 - color: "#cccccc" + width: 1 + height: 1 + color: "#8c8c8c" } orientation: Qt.Horizontal ProjectList { id: projectList - width: 200 - Layout.minimumWidth: 180 + width: 350 + Layout.minimumWidth: 250 Layout.fillHeight: true } Rectangle { @@ -154,9 +161,9 @@ Rectangle { Layout.fillWidth: true SplitView { handleDelegate: Rectangle { - width: 4 - height: 4 - color: "#cccccc" + width: 1 + height: 1 + color: "#8c8c8c" } id: codeWebSplitter anchors.fill: parent diff --git a/mix/qml/ProjectList.qml b/mix/qml/ProjectList.qml index 30f945706..65eebcdbb 100644 --- a/mix/qml/ProjectList.qml +++ b/mix/qml/ProjectList.qml @@ -2,138 +2,151 @@ import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Layouts 1.0 import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.3 +import Qt.labs.settings 1.0 +import "." Item { property bool renameMode: false; ColumnLayout { anchors.fill: parent - Text { - Layout.fillWidth: true - color: "blue" - text: projectModel.projectTitle - horizontalAlignment: Text.AlignHCenter - visible: !projectModel.isEmpty; + id: filesCol + spacing: 0 + FontLoader + { + id: srcSansProLight + source: "qrc:/qml/fonts/SourceSansPro-Regular.ttf" } - ListView { - id: projectList - Layout.fillWidth: true - Layout.fillHeight: true - model: projectModel.listModel + Rectangle + { + color: Style.title.background + height: Style.title.height + Layout.fillWidth: true + Image { + id: projectIcon + source: "qrc:/qml/img/projecticon.png" + sourceSize.height: 30 + anchors.right: projectTitle.left + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 6 + } - delegate: renderDelegate - highlight: Rectangle { - color: "lightsteelblue"; + Text + { + id: projectTitle + color: Style.title.color + text: projectModel.projectTitle + anchors.verticalCenter: parent.verticalCenter + visible: !projectModel.isEmpty; + anchors.left: parent.left + anchors.leftMargin: Style.general.leftMargin + font.family: srcSansProLight.name + font.pointSize: Style.title.pointSize + font.weight: Font.Light } - highlightFollowsCurrentItem: true - focus: true - clip: true - onCurrentIndexChanged: { - if (currentIndex >= 0 && currentIndex < projectModel.listModel.count) - projectModel.openDocument(projectModel.listModel.get(currentIndex).documentId); + Text + { + text: "-" + anchors.right: parent.right + anchors.rightMargin: 15 + font.family: srcSansProLight.name + font.pointSize: Style.title.pointSize + anchors.verticalCenter: parent.verticalCenter } } - Menu { - id: contextMenu - MenuItem { - text: qsTr("Rename") - onTriggered: { - renameMode = true; - } - } - MenuItem { - text: qsTr("Delete") - onTriggered: { - projectModel.removeDocument(projectList.model.get(projectList.currentIndex).documentId); - } - } + + Rectangle + { + Layout.fillWidth: true + height: 10 + color: Style.documentsList.background } - } - Component { - id: renderDelegate - Item { - id: wrapperItem - height: 20 - width: parent.width - RowLayout { - anchors.fill: parent - visible: !(index === projectList.currentIndex) || !renameMode - Text { - id: nameText - Layout.fillWidth: true - Layout.fillHeight: true - text: name - font.pointSize: 12 - verticalAlignment: Text.AlignBottom - } - } - TextInput { - id: textInput - text: nameText.text - visible: (index === projectList.currentIndex) && renameMode - MouseArea { - id: textMouseArea - anchors.fill: parent - hoverEnabled: true - z:2 - onClicked: { - textInput.forceActiveFocus(); - } - } - onVisibleChanged: { - if (visible) { - selectAll(); - forceActiveFocus(); - } - } - onAccepted: close(true); - onCursorVisibleChanged: { - if (!cursorVisible) - close(false); - } - onFocusChanged: { - if (!focus) - close(false); - } - function close(accept) { - renameMode = false; - if (accept) - projectModel.renameDocument(projectList.model.get(projectList.currentIndex).documentId, textInput.text); - } - } - MouseArea { - id: mouseArea - z: 1 - hoverEnabled: false - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked:{ - projectList.currentIndex = index; - if (mouse.button === Qt.RightButton && !projectList.model.get(index).isContract) - contextMenu.popup(); - } - } - } - } - Connections { - target: projectModel - onProjectLoaded: { - projectList.currentIndex = 0; - if (projectList.currentIndex >= 0 && projectList.currentIndex < projectModel.listModel.count) - projectModel.openDocument(projectModel.listModel.get(projectList.currentIndex).documentId); + Rectangle + { + Layout.fillWidth: true + Layout.fillHeight: true + color: Style.documentsList.background - } - onProjectClosed: { - projectList.currentIndex = -1; - } - onDocumentOpened: { - if (projectList.currentItem.documentId !== document.documentId) - projectList.currentIndex = projectModel.getDocumentIndex(document.documentId); + ColumnLayout + { + anchors.top: parent.top + width: parent.width + spacing: 0 + Repeater { + model: ["Contracts", "Javascript", "HTML", "Styles", "Images", "Misc"] + signal selected(string doc, string groupName) + id: sectionRepeater + FilesSection + { + sectionName: modelData + model: sectionModel + selManager: sectionRepeater + + onDocumentSelected: { + selManager.selected(doc, groupName); + } + + ListModel + { + id: sectionModel + } + + Connections { + target: codeModel + onCompilationComplete: { + if (modelData === "Contracts") + { + var ctr = projectModel.listModel.get(0); + if (codeModel.code.contract.name !== ctr.name) + { + ctr.name = codeModel.code.contract.name; + projectModel.listModel.set(0, ctr); + sectionModel.set(0, ctr); + } + } + } + } + + Connections { + id: projectModelConnection + target: projectModel + + function addDocToSubModel() + { + for (var k = 0; k < projectModel.listModel.count; k++) + { + var item = projectModel.listModel.get(k); + if (item.groupName === modelData) + sectionModel.append(item); + } + } + + onProjectLoaded: { + addDocToSubModel(); + if (modelData === "Contracts") + { + var selItem = projectModel.listModel.get(0); + projectModel.openDocument(selItem.documentId); + sectionRepeater.selected(selItem.documentId, modelData); + } + } + + onDocumentAdded: + { + var newDoc = projectModel.getDocument(documentId); + if (newDoc.groupName === modelData) + sectionModel.append(newDoc); + } + } + } + } + } } } } diff --git a/mix/qml/ProjectModel.qml b/mix/qml/ProjectModel.qml index 49e63e184..42bf91d9d 100644 --- a/mix/qml/ProjectModel.qml +++ b/mix/qml/ProjectModel.qml @@ -41,6 +41,7 @@ Item { function addExistingFile() { ProjectModelCode.addExistingFile(); } function newHtmlFile() { ProjectModelCode.newHtmlFile(); } function newJsFile() { ProjectModelCode.newJsFile(); } + function newCssFile() { ProjectModelCode.newCssFile(); } //function newContract() { ProjectModelCode.newContract(); } function openDocument(documentId) { ProjectModelCode.openDocument(documentId); } function openNextDocument() { ProjectModelCode.openNextDocument(); } diff --git a/mix/qml/Style.qml b/mix/qml/Style.qml new file mode 100644 index 000000000..91745749f --- /dev/null +++ b/mix/qml/Style.qml @@ -0,0 +1,29 @@ +pragma Singleton +import QtQuick 2.0 + +/* + * Project Files + */ +QtObject { + property QtObject general: QtObject { + property int leftMargin: 45 + } + + property QtObject title: QtObject { + property string color: "#808080" + property string background: "#f0f0f0" + property int height: 70 + property int pointSize: 18 + } + + property QtObject documentsList: QtObject { + property string background: "#f7f7f7" + property string color: "#4d4d4d" + property string sectionColor: "#808080" + property string selectedColor: "white" + property string highlightColor: "#4a90e2" + property int height: 32 + property int fileNameHeight: 45 + property int fontSize: 15 + } +} diff --git a/mix/qml/fonts/SourceSansPro-Black.ttf b/mix/qml/fonts/SourceSansPro-Black.ttf new file mode 100644 index 000000000..be1a3108e Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Black.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-BlackIt.ttf b/mix/qml/fonts/SourceSansPro-BlackIt.ttf new file mode 100644 index 000000000..ac5c4ef7c Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-BlackIt.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-Bold.ttf b/mix/qml/fonts/SourceSansPro-Bold.ttf new file mode 100644 index 000000000..f47161c6b Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Bold.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-BoldIt.ttf b/mix/qml/fonts/SourceSansPro-BoldIt.ttf new file mode 100644 index 000000000..6b3db698b Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-BoldIt.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-ExtraLight.ttf b/mix/qml/fonts/SourceSansPro-ExtraLight.ttf new file mode 100644 index 000000000..0a3e51fdb Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-ExtraLight.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-ExtraLightIt.ttf b/mix/qml/fonts/SourceSansPro-ExtraLightIt.ttf new file mode 100644 index 000000000..a0eb86aca Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-ExtraLightIt.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-It.ttf b/mix/qml/fonts/SourceSansPro-It.ttf new file mode 100644 index 000000000..fcc95fc7c Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-It.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-Light.ttf b/mix/qml/fonts/SourceSansPro-Light.ttf new file mode 100644 index 000000000..9cae13c97 Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Light.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-LightIt.ttf b/mix/qml/fonts/SourceSansPro-LightIt.ttf new file mode 100644 index 000000000..2ed784284 Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-LightIt.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-Regular.ttf b/mix/qml/fonts/SourceSansPro-Regular.ttf new file mode 100644 index 000000000..8e8255e17 Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Regular.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-Semibold.ttf b/mix/qml/fonts/SourceSansPro-Semibold.ttf new file mode 100644 index 000000000..121ee9bbf Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-Semibold.ttf differ diff --git a/mix/qml/fonts/SourceSansPro-SemiboldIt.ttf b/mix/qml/fonts/SourceSansPro-SemiboldIt.ttf new file mode 100644 index 000000000..6ceaa885f Binary files /dev/null and b/mix/qml/fonts/SourceSansPro-SemiboldIt.ttf differ diff --git a/mix/qml/fonts/SourceSerifPro-Bold.ttf b/mix/qml/fonts/SourceSerifPro-Bold.ttf new file mode 100644 index 000000000..ac7837fd9 Binary files /dev/null and b/mix/qml/fonts/SourceSerifPro-Bold.ttf differ diff --git a/mix/qml/fonts/SourceSerifPro-Regular.ttf b/mix/qml/fonts/SourceSerifPro-Regular.ttf new file mode 100644 index 000000000..7201a8890 Binary files /dev/null and b/mix/qml/fonts/SourceSerifPro-Regular.ttf differ diff --git a/mix/qml/fonts/SourceSerifPro-Semibold.ttf b/mix/qml/fonts/SourceSerifPro-Semibold.ttf new file mode 100644 index 000000000..db2fc804b Binary files /dev/null and b/mix/qml/fonts/SourceSerifPro-Semibold.ttf differ diff --git a/mix/qml/img/closedtriangleindicator_filesproject.png b/mix/qml/img/closedtriangleindicator_filesproject.png new file mode 100644 index 000000000..840f500e4 Binary files /dev/null and b/mix/qml/img/closedtriangleindicator_filesproject.png differ diff --git a/mix/qml/img/opentriangleindicator_filesproject.png b/mix/qml/img/opentriangleindicator_filesproject.png new file mode 100644 index 000000000..ee351b05c Binary files /dev/null and b/mix/qml/img/opentriangleindicator_filesproject.png differ diff --git a/mix/qml/img/projecticon.png b/mix/qml/img/projecticon.png new file mode 100644 index 000000000..58b8b6f60 Binary files /dev/null and b/mix/qml/img/projecticon.png differ diff --git a/mix/qml/js/ProjectModel.js b/mix/qml/js/ProjectModel.js index 718dd4fa4..19a56432e 100644 --- a/mix/qml/js/ProjectModel.js +++ b/mix/qml/js/ProjectModel.js @@ -88,7 +88,9 @@ function addFile(fileName) { var isHtml = extension === ".html"; var isCss = extension === ".css"; var isJs = extension === ".js"; + var isImg = extension === ".png" || extension === ".gif" || extension === ".jpg" || extension === ".svg"; var syntaxMode = isContract ? "solidity" : isJs ? "javascript" : isHtml ? "htmlmixed" : isCss ? "css" : ""; + var groupName = isContract ? "Contracts" : isJs ? "Javascript" : isHtml ? "HTML" : isCss ? "Styles" : isImg ? "Images" : "Misc"; var docData = { contract: false, path: p, @@ -99,6 +101,7 @@ function addFile(fileName) { isText: isContract || isHtml || isCss || isJs, isContract: isContract, isHtml: isHtml, + groupName: groupName }; projectListModel.append(docData); @@ -173,8 +176,8 @@ function doCreateProject(title, path) { files: [ contractsFile, indexFile ] }; //TODO: copy from template - fileIo.writeFile(dirPath + indexFile, "\n\n\n\n\n\n\n"); - fileIo.writeFile(dirPath + contractsFile, "contract Contract {\n}\n"); + fileIo.writeFile(dirPath + indexFile, "\n\n\n\n\n\n\n"); + fileIo.writeFile(dirPath + contractsFile, "contract Contract {\n}\n"); newProject(projectData); var json = JSON.stringify(projectData, null, "\t"); fileIo.writeFile(projectFile, json); @@ -225,6 +228,10 @@ function newHtmlFile() { createAndAddFile("page", "html", "\n"); } +function newCssFile() { + createAndAddFile("style", "css", "body {\n}\n"); +} + function newJsFile() { createAndAddFile("script", "js", "function foo() {\n}\n"); } diff --git a/mix/qml/main.qml b/mix/qml/main.qml index 41b7c50a4..24bd73e6d 100644 --- a/mix/qml/main.qml +++ b/mix/qml/main.qml @@ -28,6 +28,7 @@ ApplicationWindow { MenuItem { action: addExistingFileAction } MenuItem { action: addNewJsFileAction } MenuItem { action: addNewHtmlFileAction } + MenuItem { action: addNewCssFileAction } MenuSeparator {} //MenuItem { action: addNewContractAction } MenuItem { action: closeProjectAction } @@ -188,6 +189,14 @@ ApplicationWindow { onTriggered: projectModel.newHtmlFile(); } + Action { + id: addNewCssFileAction + text: qsTr("New CSS File") + shortcut: "Ctrl+Alt+S" + enabled: !projectModel.isEmpty + onTriggered: projectModel.newCssFile(); + } + Action { id: addNewContractAction text: qsTr("New Contract") diff --git a/mix/qml/qmldir b/mix/qml/qmldir new file mode 100644 index 000000000..819842274 --- /dev/null +++ b/mix/qml/qmldir @@ -0,0 +1 @@ +singleton Style 1.0 Style.qml diff --git a/mix/res.qrc b/mix/res.qrc index d73635f9d..d17c32549 100644 --- a/mix/res.qrc +++ b/mix/res.qrc @@ -63,5 +63,26 @@ res/mix_256x256x32.png qml/CallStack.qml qml/QVariableDeclaration.qml + qml/Style.qml + qml/qmldir + qml/FilesSection.qml + qml/fonts/SourceSansPro-Black.ttf + qml/fonts/SourceSansPro-BlackIt.ttf + qml/fonts/SourceSansPro-Bold.ttf + qml/fonts/SourceSansPro-BoldIt.ttf + qml/fonts/SourceSansPro-ExtraLight.ttf + qml/fonts/SourceSansPro-ExtraLightIt.ttf + qml/fonts/SourceSansPro-It.ttf + qml/fonts/SourceSansPro-Light.ttf + qml/fonts/SourceSansPro-LightIt.ttf + qml/fonts/SourceSansPro-Regular.ttf + qml/fonts/SourceSansPro-Semibold.ttf + qml/fonts/SourceSansPro-SemiboldIt.ttf + qml/fonts/SourceSerifPro-Bold.ttf + qml/fonts/SourceSerifPro-Regular.ttf + qml/fonts/SourceSerifPro-Semibold.ttf + qml/img/closedtriangleindicator_filesproject.png + qml/img/opentriangleindicator_filesproject.png + qml/img/projecticon.png