Browse Source

Merge pull request #964 from yann300/ui_improvement

Project tree redesign
cl-refactor
Gav Wood 10 years ago
parent
commit
1c87bbb7a5
  1. 243
      mix/qml/FilesSection.qml
  2. 23
      mix/qml/MainContent.qml
  3. 213
      mix/qml/ProjectList.qml
  4. 1
      mix/qml/ProjectModel.qml
  5. 29
      mix/qml/Style.qml
  6. BIN
      mix/qml/fonts/SourceSansPro-Black.ttf
  7. BIN
      mix/qml/fonts/SourceSansPro-BlackIt.ttf
  8. BIN
      mix/qml/fonts/SourceSansPro-Bold.ttf
  9. BIN
      mix/qml/fonts/SourceSansPro-BoldIt.ttf
  10. BIN
      mix/qml/fonts/SourceSansPro-ExtraLight.ttf
  11. BIN
      mix/qml/fonts/SourceSansPro-ExtraLightIt.ttf
  12. BIN
      mix/qml/fonts/SourceSansPro-It.ttf
  13. BIN
      mix/qml/fonts/SourceSansPro-Light.ttf
  14. BIN
      mix/qml/fonts/SourceSansPro-LightIt.ttf
  15. BIN
      mix/qml/fonts/SourceSansPro-Regular.ttf
  16. BIN
      mix/qml/fonts/SourceSansPro-Semibold.ttf
  17. BIN
      mix/qml/fonts/SourceSansPro-SemiboldIt.ttf
  18. BIN
      mix/qml/fonts/SourceSerifPro-Bold.ttf
  19. BIN
      mix/qml/fonts/SourceSerifPro-Regular.ttf
  20. BIN
      mix/qml/fonts/SourceSerifPro-Semibold.ttf
  21. BIN
      mix/qml/img/closedtriangleindicator_filesproject.png
  22. BIN
      mix/qml/img/opentriangleindicator_filesproject.png
  23. BIN
      mix/qml/img/projecticon.png
  24. 7
      mix/qml/js/ProjectModel.js
  25. 9
      mix/qml/main.qml
  26. 1
      mix/qml/qmldir
  27. 21
      mix/res.qrc

243
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);
}
}
}
}
}
}
}

23
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

213
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
delegate: renderDelegate
highlight: Rectangle {
color: "lightsteelblue";
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
}
highlightFollowsCurrentItem: true
focus: true
clip: true
onCurrentIndexChanged: {
if (currentIndex >= 0 && currentIndex < projectModel.listModel.count)
projectModel.openDocument(projectModel.listModel.get(currentIndex).documentId);
}
}
Menu {
id: contextMenu
MenuItem {
text: qsTr("Rename")
onTriggered: {
renameMode = true;
}
}
MenuItem {
text: qsTr("Delete")
onTriggered: {
projectModel.removeDocument(projectList.model.get(projectList.currentIndex).documentId);
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
}
Text
{
text: "-"
anchors.right: parent.right
anchors.rightMargin: 15
font.family: srcSansProLight.name
font.pointSize: Style.title.pointSize
anchors.verticalCenter: parent.verticalCenter
}
}
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
Rectangle
{
Layout.fillWidth: true
Layout.fillHeight: true
text: name
font.pointSize: 12
verticalAlignment: Text.AlignBottom
color: Style.documentsList.background
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
}
TextInput {
id: textInput
text: nameText.text
visible: (index === projectList.currentIndex) && renameMode
MouseArea {
id: textMouseArea
anchors.fill: parent
hoverEnabled: true
z:2
onClicked: {
textInput.forceActiveFocus();
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);
}
}
onVisibleChanged: {
if (visible) {
selectAll();
forceActiveFocus();
}
}
onAccepted: close(true);
onCursorVisibleChanged: {
if (!cursorVisible)
close(false);
}
onFocusChanged: {
if (!focus)
close(false);
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);
}
function close(accept) {
renameMode = false;
if (accept)
projectModel.renameDocument(projectList.model.get(projectList.currentIndex).documentId, textInput.text);
}
onProjectLoaded: {
addDocToSubModel();
if (modelData === "Contracts")
{
var selItem = projectModel.listModel.get(0);
projectModel.openDocument(selItem.documentId);
sectionRepeater.selected(selItem.documentId, modelData);
}
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();
}
onDocumentAdded:
{
var newDoc = projectModel.getDocument(documentId);
if (newDoc.groupName === modelData)
sectionModel.append(newDoc);
}
}
}
Connections {
target: projectModel
onProjectLoaded: {
projectList.currentIndex = 0;
if (projectList.currentIndex >= 0 && projectList.currentIndex < projectModel.listModel.count)
projectModel.openDocument(projectModel.listModel.get(projectList.currentIndex).documentId);
}
onProjectClosed: {
projectList.currentIndex = -1;
}
onDocumentOpened: {
if (projectList.currentItem.documentId !== document.documentId)
projectList.currentIndex = projectModel.getDocumentIndex(document.documentId);
}
}
}

1
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(); }

29
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
}
}

BIN
mix/qml/fonts/SourceSansPro-Black.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-BlackIt.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-Bold.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-BoldIt.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-ExtraLight.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-ExtraLightIt.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-It.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-Light.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-LightIt.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-Regular.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-Semibold.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSansPro-SemiboldIt.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSerifPro-Bold.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSerifPro-Regular.ttf

Binary file not shown.

BIN
mix/qml/fonts/SourceSerifPro-Semibold.ttf

Binary file not shown.

BIN
mix/qml/img/closedtriangleindicator_filesproject.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

BIN
mix/qml/img/opentriangleindicator_filesproject.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

BIN
mix/qml/img/projecticon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

7
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);
@ -225,6 +228,10 @@ function newHtmlFile() {
createAndAddFile("page", "html", "<html>\n</html>");
}
function newCssFile() {
createAndAddFile("style", "css", "body {\n}\n");
}
function newJsFile() {
createAndAddFile("script", "js", "function foo() {\n}\n");
}

9
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")

1
mix/qml/qmldir

@ -0,0 +1 @@
singleton Style 1.0 Style.qml

21
mix/res.qrc

@ -63,5 +63,26 @@
<file>res/mix_256x256x32.png</file>
<file>qml/CallStack.qml</file>
<file>qml/QVariableDeclaration.qml</file>
<file>qml/Style.qml</file>
<file>qml/qmldir</file>
<file>qml/FilesSection.qml</file>
<file>qml/fonts/SourceSansPro-Black.ttf</file>
<file>qml/fonts/SourceSansPro-BlackIt.ttf</file>
<file>qml/fonts/SourceSansPro-Bold.ttf</file>
<file>qml/fonts/SourceSansPro-BoldIt.ttf</file>
<file>qml/fonts/SourceSansPro-ExtraLight.ttf</file>
<file>qml/fonts/SourceSansPro-ExtraLightIt.ttf</file>
<file>qml/fonts/SourceSansPro-It.ttf</file>
<file>qml/fonts/SourceSansPro-Light.ttf</file>
<file>qml/fonts/SourceSansPro-LightIt.ttf</file>
<file>qml/fonts/SourceSansPro-Regular.ttf</file>
<file>qml/fonts/SourceSansPro-Semibold.ttf</file>
<file>qml/fonts/SourceSansPro-SemiboldIt.ttf</file>
<file>qml/fonts/SourceSerifPro-Bold.ttf</file>
<file>qml/fonts/SourceSerifPro-Regular.ttf</file>
<file>qml/fonts/SourceSerifPro-Semibold.ttf</file>
<file>qml/img/closedtriangleindicator_filesproject.png</file>
<file>qml/img/opentriangleindicator_filesproject.png</file>
<file>qml/img/projecticon.png</file>
</qresource>
</RCC>

Loading…
Cancel
Save