Browse Source

Refactor graph part

Rendering based on canvas
Alexey Edelev 8 years ago
parent
commit
52bb6cad51
13 changed files with 265 additions and 230 deletions
  1. 2 2
      NiceGit.pro
  2. 34 36
      commitgraph.cpp
  3. 12 4
      commitgraph.h
  4. 0 6
      commitviewmodel.cpp
  5. 0 39
      commitviewmodel.h
  6. 0 3
      gitcommit.cpp
  7. 1 12
      gitcommit.h
  8. 7 10
      githandler.cpp
  9. 21 0
      githandler.h
  10. 52 0
      graphpoint.cpp
  11. 75 0
      graphpoint.h
  12. 7 3
      main.cpp
  13. 54 115
      qml/MainView.qml

+ 2 - 2
NiceGit.pro

@@ -17,7 +17,7 @@ SOURCES += \
     commitmodel.cpp \
     universallistmodel.cpp \
     commitgraph.cpp \
-    commitviewmodel.cpp
+    graphpoint.cpp
 
 HEADERS += \
     githandler.h \
@@ -31,7 +31,7 @@ HEADERS += \
     commitmodel.h \
     universallistmodel.h \
     commitgraph.h \
-    commitviewmodel.h
+    graphpoint.h
 
 RESOURCES += \
     resources.qrc

+ 34 - 36
commitgraph.cpp

@@ -1,11 +1,13 @@
 #include "commitgraph.h"
 
-#include <git2/revwalk.h>
-#include <git2/commit.h>
-
 #include <gitcommit.h>
+#include <graphpoint.h>
+
 #include <QDateTime>
 
+#include <git2/revwalk.h>
+#include <git2/commit.h>
+
 CommitGraph::CommitGraph() : QObject()
 {
     qsrand(QDateTime::currentMSecsSinceEpoch());
@@ -13,12 +15,11 @@ CommitGraph::CommitGraph() : QObject()
 
 void CommitGraph::addHead(const GitOid &oid)
 {
+    //Random color generation to be replaced with resets
     int red = qrand() % 205 + 50;
     int green = qrand() % 205 + 50;
     int blue = qrand() % 205 + 50;
-
     m_color = QString::number(red, 16) + QString::number(green, 16) + QString::number(blue, 16);
-    qDebug() << m_color;
 
     git_revwalk* walk;
     git_revwalk_new(&walk, oid.repository()->raw());
@@ -28,13 +29,17 @@ void CommitGraph::addHead(const GitOid &oid)
 
     git_oid newOid;
     while(git_revwalk_next(&newOid, walk) == 0) {
-        qDebug() << "Next commit parents";
         GitOid commitOid(&newOid, oid.repository());
         GitCommit *commit = GitCommit::fromOid(commitOid);
         findParents(commit);
     }
 
     git_revwalk_free(walk);
+
+    qDebug() << "Update Y coordinate after head added";
+    for(int i = 0; i < m_sortedPoints.count(); i++) {
+        static_cast<GraphPoint*>(m_sortedPoints.at(i))->setY(i);
+    }
 }
 
 void CommitGraph::findParents(GitCommit* commit)
@@ -47,15 +52,16 @@ void CommitGraph::findParents(GitCommit* commit)
         GitOid parentOid(git_commit_id(commitRaw), commit->repository());
         commit = GitCommit::fromOid(parentOid);
         reverseList.push_front(parentOid);
+        if(m_points.contains(parentOid)) { //Finish parents lookup once parent found in tree. We will see nothing new in this branch
+            break;
+        }
+
         qDebug() << "Add commit to reverselist" << parentOid.toString();
         commitRaw = nullptr;
         git_commit_parent(&commitRaw, commit->raw(), 0);
-        if(m_commits.contains(parentOid)) { //Finish parents lookup once parent found in tree. We will see nothing new in this branch
-            break;
-        }
     }
 
-    if(reverseList.isEmpty()) {
+    if(reverseList.count() < 2) { //In case if only original commit in list, we didn't find anything new
         return;
     }
     addCommits(reverseList);
@@ -63,43 +69,35 @@ void CommitGraph::findParents(GitCommit* commit)
 
 void CommitGraph::addCommits(QList<GitOid>& reversList)
 {
-    GitCommit* parent;
-    GitCommit* commit;
+    GraphPoint* point = nullptr;
+    GraphPoint* parentPoint = nullptr;
 
     for(int i = 0; i < (reversList.count() - 1); i++) {
         GitOid& parentIter = reversList[i];
         GitOid& childIter = reversList[i + 1];
-        parent = m_commits.value(parentIter, nullptr);
-        if(parent == nullptr) {
-            //Ony in case if i == 0
-            parent = GitCommit::fromOid(parentIter);
-            m_commits.insert(parent->oid(), parent);
-            parent->m_color = m_color;
-            m_fullList.push_back(parent);
+        parentPoint = m_points.value(parentIter, nullptr);
+        if(parentPoint == nullptr) {
+            parentPoint = new GraphPoint(parentIter, this);
+            parentPoint->setColor(m_color);
+            m_sortedPoints.append(parentPoint);
+            m_points.insert(parentPoint->oid(), parentPoint);
         }
 
-        commit = m_commits.value(childIter, nullptr);
-        if(commit == nullptr) {
-            commit = GitCommit::fromOid(childIter);
-
-            //ViewModelPart
-            commit->m_x = parent->m_childrenCounter++;
-            commit->m_childrenCounter = commit->m_x;
-            commit->m_color = m_color;
-            if(commit->m_x == parent->m_x) { //TODO: Too dirty hack seems will not work with amount of commits more than 1
-                parent->m_color = commit->m_color;
-            }
-            //End ViewModelPart
+        point = m_points.value(childIter, nullptr);
+        if(point == nullptr) {
+            int x = parentPoint->x() + parentPoint->childPointsCount();
+            point = new GraphPoint(childIter, x, 0, m_color, this);
+            parentPoint->addChildPoint(point);
 
-            m_commits.insert(commit->oid(), commit);
+            m_points.insert(point->oid(), point);
 
             //Ordered commits
-            int parentPosition = m_fullList.indexOf(parent);
+            int parentPosition = m_sortedPoints.indexOf(parentPoint);
             if(parentPosition >= 0) {
-                m_fullList.insert(parentPosition + 1, commit);
+                m_sortedPoints.insert(parentPosition + 1, point);
             }
-            qDebug() << "New commit: " << commit->sha1() << commit->x() << commit->y();
-            qDebug() << "Parent commit: " << parent->sha1() << parent->x() << parent->y();
+            qDebug() << "New commit: " << point->oid().toString() << point->x() << point->y();
+            qDebug() << "New commit: " << parentPoint->oid().toString() << parentPoint->x() << parentPoint->y();
         }
     }
 }

+ 12 - 4
commitgraph.h

@@ -3,27 +3,35 @@
 
 #include <QObject>
 
+#include <gitoid.h>
+
 #include <QString>
 #include <QHash>
-#include <gitoid.h>
+#include <QObjectList>
 
 class GitCommit;
+class GraphPoint;
 
 class CommitGraph : public QObject
 {
+    Q_OBJECT
+    Q_PROPERTY(QList<QObject*> points READ points CONSTANT)
 public:
     CommitGraph();
     void addHead(const GitOid& oid);
 
-    QHash<GitOid, GitCommit*> m_commits;
-    QList<GitCommit*> m_fullList;
+    QList<QObject*> points() const {
+        return m_sortedPoints;
+    }
 
 private:
     void findParents(GitCommit *commit);
-
     void addCommits(QList<GitOid> &reversList);
 
     QString m_color;
+
+    QHash<GitOid, GraphPoint*> m_points;
+    QObjectList m_sortedPoints;
 };
 
 #endif // COMMITGRAPH_H

+ 0 - 6
commitviewmodel.cpp

@@ -1,6 +0,0 @@
-#include "commitviewmodel.h"
-
-CommitViewModel::CommitViewModel(CommitGraph *graph)
-{
-
-}

+ 0 - 39
commitviewmodel.h

@@ -1,39 +0,0 @@
-#ifndef COMMITVIEWMODEL_H
-#define COMMITVIEWMODEL_H
-
-#include <QObject>
-
-class CommitGraph;
-
-class CommitViewModel : public QObject
-{
-    Q_OBJECT
-    Q_PROPERTY(int x READ x CONSTANT)
-    Q_PROPERTY(int y READ y CONSTANT)
-    Q_PROPERTY(QString color READ color CONSTANT)
-
-public:
-    CommitViewModel(CommitGraph* graph);
-
-    int x() const
-    {
-        return m_x;
-    }
-
-    int y() const
-    {
-        return m_y;
-    }
-
-    QString color() const
-    {
-        return m_color;
-    }
-
-private:
-    int m_x;
-    int m_y;
-    QString m_color;
-};
-
-#endif // COMMITVIEWMODEL_H

+ 0 - 3
gitcommit.cpp

@@ -7,9 +7,6 @@
 #include <git2/commit.h>
 
 GitCommit::GitCommit(git_commit* raw, GitRepository* parent) : GitBase(raw, parent)
-  ,m_x(0)
-  ,m_y(0)
-  ,m_childrenCounter(0)
 {
     m_oid = GitOid(git_commit_id(m_raw), m_repository);
 }

+ 1 - 12
gitcommit.h

@@ -5,6 +5,7 @@
 
 #include <QDateTime>
 #include <QString>
+#include <QList>
 
 #include <git2/types.h>
 
@@ -18,9 +19,6 @@ class GitCommit : public GitBase<git_commit>
     Q_PROPERTY(QString sha1 READ sha1 NOTIFY commitChanged)
     Q_PROPERTY(QString shortSha1 READ shortSha1 NOTIFY commitChanged)
     Q_PROPERTY(bool isMerge READ isMerge NOTIFY commitChanged)
-    Q_PROPERTY(int x READ x NOTIFY commitChanged)
-    Q_PROPERTY(int y READ y NOTIFY commitChanged)
-    Q_PROPERTY(QString color READ color)
 
 public:
     GitCommit(git_commit* raw, GitRepository* parent);
@@ -35,9 +33,6 @@ public:
     QString sha1() const;
     QString shortSha1() const;
     bool isMerge() const;
-    int x() const { return m_x; }
-    int y() const { return m_y; }
-    QString color() const { return m_color; }
 
 public slots:
     void setAuthor(QString author);
@@ -55,12 +50,6 @@ private:
     QDateTime m_time;
     QString m_message;
     QString m_email;
-
-public:
-    int m_x;
-    int m_y;
-    int m_childrenCounter;
-    QString m_color;
 };
 
 #endif // GITCOMMIT_H

+ 7 - 10
githandler.cpp

@@ -10,6 +10,7 @@
 #include <git2.h>
 
 #include <commitgraph.h>
+#include <graphpoint.h>
 
 GitHandler::GitHandler() : QObject()
   ,m_repositories(new RepositoryModel(this))
@@ -52,16 +53,12 @@ void GitHandler::open(const QString &path)
         graph->addHead(branch->oid());
     }
 
-    foreach (GitCommit* commit, graph->m_fullList) {
-        qDebug() << commit->sha1();
-        commit->m_y = graph->m_fullList.indexOf(commit);
-    }
-
-    CommitModel* main = new CommitModel("main");
-    foreach (GitCommit* commitPtr, graph->m_commits) {
-        main->add(commitPtr);
-    }
-    m_commits.insert("main", main);
+    setGraph(graph);
+//    CommitModel* main = new CommitModel("main");
+//    foreach (GitCommit* commitPtr, graph->m_points) {
+//        main->add(commitPtr);
+//    }
+//    m_commits.insert("main", main);
     m_repositories->add(repo);
 }
 

+ 21 - 0
githandler.h

@@ -6,12 +6,15 @@
 #include <repositorymodel.h>
 
 class CommitModel;
+class CommitGraph;
+
 typedef QHash<QString, QPointer<CommitModel>> CommitModelContainer;
 
 class GitHandler : public QObject
 {
     Q_OBJECT
     Q_PROPERTY(RepositoryModel* repositories READ repositories NOTIFY repositoriesChanged)
+    Q_PROPERTY(CommitGraph* graph READ graph WRITE setGraph NOTIFY graphChanged)
 
 public:
     GitHandler();
@@ -26,15 +29,33 @@ public:
 
     Q_INVOKABLE CommitModel* modelByHead(const QString& head);
 
+    CommitGraph* graph() const
+    {
+        return m_graph;
+    }
+
+public slots:
+    void setGraph(CommitGraph* graph)
+    {
+        if (m_graph == graph)
+            return;
+
+        m_graph = graph;
+        emit graphChanged(graph);
+    }
+
 signals:
     void repositoriesChanged(RepositoryModel* repositories);
 
+    void graphChanged(CommitGraph* graph);
+
 protected:
     QString lastError() const;
 
 private:
     RepositoryModel* m_repositories;
     CommitModelContainer m_commits;
+    CommitGraph* m_graph;
 };
 
 #endif // GITHANDLER_H

+ 52 - 0
graphpoint.cpp

@@ -0,0 +1,52 @@
+#include "graphpoint.h"
+
+GraphPoint::GraphPoint(const GitOid &commitOid, QObject* parent) : QObject(parent)
+  ,m_commitOid(commitOid)
+  ,m_x(0)
+  ,m_y(0)
+{
+
+}
+
+GraphPoint::GraphPoint(const GitOid &commitOid, int x, int y, const QString &color, QObject *parent) : QObject(parent)
+  ,m_commitOid(commitOid)
+  ,m_x(x)
+  ,m_y(y)
+  ,m_color(color)
+{
+}
+
+GraphPoint::~GraphPoint()
+{
+}
+
+void GraphPoint::setX(int x)
+{
+    if (m_x == x) {
+        return;
+    }
+
+    m_x = x;
+    emit xChanged(x);
+}
+
+void GraphPoint::setY(int y)
+{
+    if (m_y == y) {
+        return;
+    }
+
+    m_y = y;
+    emit yChanged(y);
+}
+
+void GraphPoint::setColor(const QString& color)
+{
+    if (m_color == color) {
+        return;
+    }
+
+    m_color = color;
+    emit colorChanged(color);
+}
+

+ 75 - 0
graphpoint.h

@@ -0,0 +1,75 @@
+#ifndef GRAPHPOINT_H
+#define GRAPHPOINT_H
+
+#include <QList>
+#include <QObject>
+#include <gitoid.h>
+
+class GraphPoint : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged)
+    Q_PROPERTY(int y READ y WRITE setY NOTIFY yChanged)
+    Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
+    Q_PROPERTY(QList<QObject*> childPoints READ childPoints CONSTANT)
+
+public:
+    GraphPoint(const GitOid& commitOid, QObject* parent = 0);
+    GraphPoint(const GitOid& commitOid, int x, int y, const QString& color, QObject* parent = 0);
+    ~GraphPoint();
+
+    int x() const
+    {
+        return m_x;
+    }
+
+    int y() const
+    {
+        return m_y;
+    }
+
+    QString color() const
+    {
+        return m_color;
+    }
+
+    const GitOid& oid() const
+    {
+        return m_commitOid;
+    }
+
+    int childPointsCount() const
+    {
+        return m_childPoints.count();
+    }
+
+    void addChildPoint(GraphPoint* point)
+    {
+        m_childPoints.append(point);
+    }
+
+    QList<QObject*> childPoints() const
+    {
+        return m_childPoints;
+    }
+
+public slots:
+    void setX(int x);
+    void setY(int y);
+    void setColor(const QString& color);
+
+signals:
+    void xChanged(int x);
+    void yChanged(int y);
+    void colorChanged(const QString& color);
+
+private:
+    GitOid m_commitOid;
+    QList<QObject*> m_childPoints;
+
+    int m_x;
+    int m_y;
+    QString m_color;
+};
+
+#endif // GRAPTHPOINT_H

+ 7 - 3
main.cpp

@@ -8,6 +8,8 @@
 #include <repositorymodel.h>
 #include <gitbranch.h>
 #include <commitmodel.h>
+#include <graphpoint.h>
+#include <commitgraph.h>
 
 #include <QDebug>
 
@@ -17,11 +19,13 @@ int main(int argc, char *argv[])
 
     QQuickView view;
 
-    qmlRegisterUncreatableType<GitHandler>("org.semlanik.nicegit", 1, 0, "GitHandler", "Global for qml");
+    qmlRegisterUncreatableType<CommitModel>("org.semlanik.nicegit", 1, 0, "CommitModel", "Owned only by GitHandler");
+    qmlRegisterUncreatableType<CommitGraph>("org.semlanik.nicegit", 1, 0, "CommitGraph", "Owned only by GitHandler");
+    qmlRegisterUncreatableType<GraphPoint>("org.semlanik.nicegit", 1, 0, "GraphPoint", "Owned only by GitHandler");
+    qmlRegisterUncreatableType<RepositoryModel>("org.semlanik.nicegit", 1, 0, "RepositoryModel", "Owned only by GitHandler");
     qmlRegisterUncreatableType<GitRepository>("org.semlanik.nicegit", 1, 0, "GitRepository", "Owned only by GitHandler");
     qmlRegisterUncreatableType<GitBranch>("org.semlanik.nicegit", 1, 0, "GitRepository", "Owned only by GitHandler");
-    qmlRegisterUncreatableType<RepositoryModel>("org.semlanik.nicegit", 1, 0, "RepositoryModel", "Owned only by GitHandler");
-    qmlRegisterUncreatableType<CommitModel>("org.semlanik.nicegit", 1, 0, "CommitModel", "Owned only by GitHandler");
+    qmlRegisterUncreatableType<GitHandler>("org.semlanik.nicegit", 1, 0, "GitHandler", "Global for qml");
 
 
     GitHandler handler;

+ 54 - 115
qml/MainView.qml

@@ -27,129 +27,68 @@ Item {
         }
     }
 
-    Item {
-    width:  childrenRect.width
-    height: childrenRect.height
-    anchors.right: parent.right
-    Repeater {
-        model: _handler.modelByHead("main")
-        Rectangle {
-            radius: 10
-            x: model.x*(width+20)
-            y: model.y*(height+10)
-            width: 100
-            height: 30
-            color: "#"+model.color;
-            Text {
-                id: sha1Lable
-                maximumLineCount: 8
-                text: model.shortSha1
-            }
-            MouseArea {
-                hoverEnabled: true
-                anchors.fill: parent
-                onEntered: {
-                    parent.state = "full"
-                }
-                onExited: {
-                    parent.state = "short"
-                }
-            }
-        }
-    }
-}
-//    Row {
+//    Item {
+//        width:  childrenRect.width
+//        height: childrenRect.height
 //        anchors.right: parent.right
-//        anchors.top: parent.top
-//        anchors.bottom: parent.bottom
 //        Repeater {
-//            model: ListModel {
-//                ListElement {
-//                    branch: "b3"
-//                }
-//                ListElement {
-//                    branch: "master"
-//                }
-//                ListElement {
-//                    branch: "b1"
-//                }
-//                ListElement {
-//                    branch: "b2"
+//            model: _handler.graph.points
+//            Rectangle {
+//                radius: 10
+//                x: model.modelData.x*(width+20)
+//                y: model.modelData.y*(height+10)
+//                width: 100
+//                height: 30
+//                color: "#"+model.modelData.color;
+//                MouseArea {
+//                    hoverEnabled: true
+//                    anchors.fill: parent
+//                    onEntered: {
+//                        parent.state = "full"
+//                    }
+//                    onExited: {
+//                        parent.state = "short"
+//                    }
 //                }
-//                ListElement {
-//                    branch: "b4"
+//                Component.onCompleted: {
+//                    console.log("model.x: " + _handler.graph.points.length)
 //                }
 //            }
+//        }
+//    }
 
-//            Column {
-//                width: 200
-//                Text {
-//                    id: branchName
-//                    text: "Branch:" + branch
-//                }
-//                ListView {
-//                    id: branchList
-//                    height: root.height
-//                    width: 200
-//                    model: _handler.modelByHead(branch)
-//                    delegate: Rectangle {
-//                        id: idRect
-//                        width: parent.width
-//                        height: 80
-//                        color: "#cccccc"
-//                        states: [
-//                            State {
-//                                name:"full"
-//                                PropertyChanges {
-//                                    target: sha1Lable
-//                                    text: model.sha1
-//                                }
-//                                PropertyChanges {
-//                                    target: idRect
-//                                    color: !model.isMerge ? "#dddddd" : "#aa6666"
-//                                }
-//                            },
-//                            State {
-//                                name:"short"
-//                                PropertyChanges {
-//                                    target: sha1Lable
-//                                    text: model.shortSha1
-//                                }
-//                                PropertyChanges {
-//                                    target: idRect
-//                                    color: "#cccccc"
-//                                }
-//                            }]
+    Canvas {
+        property int elementWidth: 20
+        property int elementHeight: 20
+        width: parent.width/2
+        height: _handler.graph.points.length*(elementWidth + 10)
+        onPaint: {
+            var ctx = getContext("2d")
+            for(var i = 0; i < _handler.graph.points.length; i++) {
+                var point = _handler.graph.points[i]
 
-//                        state: "short"
+                ctx.beginPath()
+                ctx.fillStyle = "#"+point.color
+                ctx.roundedRect(point.x*(elementWidth + 10), point.y*(elementHeight + 10), elementWidth, elementHeight, elementWidth, elementHeight)
+                ctx.fill()
+                ctx.closePath()
+
+                var childPoints = point.childPoints
+
+                for(var j = 0; j < childPoints.length; j++) {
+                    var childPoint = childPoints[j]
+                    ctx.beginPath()
+                    ctx.strokeStyle = "#"+point.color
+                    ctx.lineWidth = 2
+                    ctx.moveTo(point.x*(elementWidth + 10) + elementWidth/2, point.y*(elementHeight + 10) + elementHeight/2)
+                    ctx.lineTo(childPoint.x*(elementWidth + 10) + elementWidth/2, childPoint.y*(elementHeight + 10) + elementHeight/2)
+                    ctx.stroke()
+                    ctx.closePath()
+                }
+            }
+        }
+    }
 
-//                        Column {
-//                            width: branchList.width
-//                            Text {
-//                                id: sha1Lable
-//                                width: branchList.width
-//                                maximumLineCount: 8
-//                                text: model.shortSha1
-//                            }
-//                            Text {
-//                                text: model.message
-//                            }
-//                        }
-//                        MouseArea {
-//                            hoverEnabled: true
-//                            anchors.fill: parent
-//                            onEntered: {
-//                                parent.state = "full"
-//                            }
-//                            onExited: {
-//                                parent.state = "short"
-//                            }
-//                        }
-//                    }
-//                }
-//            }
-//        }
-//    }
     FileDialog {
         id: repoOpenDialog
         folder: "."