~dpm/ubuntu-rssreader-app/enable-translations

« back to all changes in this revision

Viewing changes to OrganicGrid.qml

  • Committer: Tarmac
  • Author(s): Joey Chan
  • Date: 2013-09-03 16:05:14 UTC
  • mfrom: (55.1.1 ubuntu-rssreader-app)
  • Revision ID: tarmac-20130903160514-2bdefz78lg9jfrfq
what's new:
1. new grid view with new layout algorithm;
2. article items updated;

to do:
1. memory management of the new grid view;
2. read and unread indicate (need to modify the database first);
3. order articles;. Fixes: https://bugs.launchpad.net/bugs/1215343, https://bugs.launchpad.net/bugs/1218201, https://bugs.launchpad.net/bugs/1218204.

Approved by Ubuntu Phone Apps Jenkins Bot, Roman Shchekin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import QtQuick 2.0
 
2
import QtGraphicalEffects 1.0
 
3
import Ubuntu.Components 0.1
 
4
import "databasemodule_v2.js" as DB
 
5
import "./imgSeparator.js" as ImageSeparator
 
6
 
 
7
Item {
 
8
    id: organicGridRoot
 
9
 
 
10
    anchors.fill: parent
 
11
 
 
12
    property variant rectangleList: []
 
13
    property int tag_id
 
14
    property bool isAll: false
 
15
    property variant feedArray: []
 
16
 
 
17
    /*!
 
18
      * \brief check collision of two article items
 
19
      *
 
20
      */
 
21
    function checkCollision(rect1, rect2) {
 
22
        return !((rect1.y + rect1.height <= rect2.y) ||
 
23
                 (rect2.y + rect2.height <= rect1.y) ||
 
24
                 (rect1.x >= rect2.x + rect2.width) ||
 
25
                 (rect2.x >= rect1.x + rect1.width))
 
26
    }
 
27
 
 
28
    /*!
 
29
      * \brief Returns a margin within given numbers. Used to make the view
 
30
      * look more organic.
 
31
      */
 
32
    function randomMargin() {
 
33
        return units.gu(2 /*3 + Math.random() * 4*/);
 
34
    }
 
35
 
 
36
    /*!
 
37
      * Adds a given article to the model. Also adds metadata about the
 
38
      * model to the article.
 
39
      */
 
40
    function addArticleToModel(article) {
 
41
        article.model = articleModel
 
42
        article.modelIndex = articleModel.count
 
43
        articleModel.append(article)
 
44
    }
 
45
 
 
46
 
 
47
    /*!
 
48
      * Clears all articles from the model and the view.
 
49
      */
 
50
    function clear() {
 
51
        for(var i in rectangleList) {
 
52
            rectangleList[i].destroy()
 
53
        }
 
54
 
 
55
        articleModel.clear()
 
56
        rectangleList = []
 
57
    }
 
58
 
 
59
    /*!
 
60
      * Reloads the organic view. See inline comments for details.
 
61
      */
 
62
    function reload() {
 
63
        clear()
 
64
        // Decide if we should load all articles in our database or a subset.
 
65
        if (!isAll)
 
66
        {
 
67
            for (var j=0; j < feedArray.length ; j++)
 
68
            {
 
69
                var feedArticles = DB.loadArticles({"isAll": false, "feedId": feedArray[j].id});
 
70
                for(var i = 0; i < feedArticles.rows.length; i++) {
 
71
                    addArticleToModel(feedArticles.rows[i])
 
72
                }
 
73
            }
 
74
        } else {
 
75
            var feedArticles = DB.loadArticles({"isAll": true});
 
76
            for (var i=0; i < feedArticles.rows.length; i++)
 
77
            {
 
78
                addArticleToModel(feedArticles.rows[i])
 
79
            }
 
80
        }
 
81
 
 
82
        // Initial values for the starting column of items
 
83
        var xEdge = 0;
 
84
        var lastColumnX = 0;
 
85
        var y = randomMargin();
 
86
        // A hard-coded sequence that gives an organic look, but is not
 
87
        // completely random
 
88
        var sequence = [0,1,0,1,2,1,0,0,2,1,0,1,0,0];
 
89
        // TODO: Remove hard limit on 300 items
 
90
        for(var i = 0; i < articleModel.count; i++) {
 
91
            var article = articleModel.get(i);
 
92
//            var imageArray = ImageSeparator.separate(article.content)
 
93
            var imageArray = [article.image]
 
94
//            console.log("imageArray: ", imageArray)
 
95
            var hasImage = (article.image !== "");
 
96
 
 
97
            // Pick the type of size we will use for this item from the sequence
 
98
            var sizeType = sequence[i % sequence.length];
 
99
            var alignType = parseInt(Math.random() * 2)
 
100
//            if(hasImage) {
 
101
//                alignType = parseInt(Math.random() * 4);
 
102
//            } else {
 
103
//                alignType = parseInt(Math.random() * 2);
 
104
//            }
 
105
 
 
106
            var alignName
 
107
 
 
108
            // Name of the QML file to be used
 
109
            var componentName
 
110
            var noImageName = ""
 
111
            if(!hasImage) {
 
112
                switch(alignType) {
 
113
                case 0:
 
114
                    alignName = "TextA"
 
115
                    break;
 
116
                case 1:
 
117
                    alignName = "TextB"
 
118
                    break;
 
119
                }
 
120
                noImageName += "NoImage";
 
121
                sizeType = 0;
 
122
            }
 
123
            else {
 
124
                switch(alignType) {
 
125
                case 0:
 
126
                    alignName = "OneImgA"
 
127
                    break;
 
128
                case 1:
 
129
                    alignName = "FullImg"
 
130
                    break;
 
131
                }
 
132
            }
 
133
            componentName = "article_items/" + "Article" + alignName + ".qml";
 
134
 
 
135
            // create component for an article
 
136
            var component = Qt.createComponent(componentName);
 
137
            if(component.status !== Component.Ready) {
 
138
                console.log("Error loading component:", component.errorString());
 
139
                continue; // Skip if we for some reason could not create this component
 
140
            }
 
141
 
 
142
            var properties = {
 
143
                "rss_item": article,
 
144
                "imageArray": imageArray,
 
145
                "rss_model": articleModel,
 
146
                "model_index": article.modelIndex
 
147
            }
 
148
 
 
149
            // This is the actual item that will be on display
 
150
            var articleItem = component.createObject(itemContainer /*organicFlickable.contentItem*/, properties);
 
151
 
 
152
            if(y + articleItem.height > organicGridRoot.height)  {
 
153
                y = randomMargin();
 
154
                xEdge = lastColumnX;
 
155
            }
 
156
 
 
157
            // The xEdge is where the last "column" of items were placed last
 
158
            // This keeps us moving forward in x-direction
 
159
            var x = xEdge + randomMargin();
 
160
 
 
161
            // We add some extra random margin to make it look a bit more organic
 
162
            x += randomMargin();
 
163
 
 
164
            articleItem.x = x
 
165
            articleItem.y = y
 
166
 
 
167
            // Check for collisions with all other items
 
168
            // TODO: See if this may be limited to only the previous two colums
 
169
            for(var j = 0; j < rectangleList.length; j++) {
 
170
                var rect2 = rectangleList[j]
 
171
                if(checkCollision(articleItem, rect2)) {
 
172
                    articleItem.x = rect2.x + rect2.width + randomMargin();
 
173
                }
 
174
            }
 
175
 
 
176
            // Did we move the x-edge value further to the right than before?
 
177
            lastColumnX = Math.max(articleItem.x, lastColumnX);
 
178
 
 
179
            // Push this item to all lists and all that stuff
 
180
            var list = rectangleList
 
181
            list.push(articleItem)
 
182
            rectangleList = list
 
183
 
 
184
            // Increment y for the next item
 
185
            y += articleItem.height + randomMargin();
 
186
        }
 
187
        organicFlickable.scrollToStart()
 
188
    }
 
189
 
 
190
    /*!
 
191
      * use Flickable as articles container
 
192
      */
 
193
    Flickable {
 
194
        id: organicFlickable
 
195
        anchors.fill: parent
 
196
        contentWidth: itemContainer.width /*contentItem.childrenRect.width*/
 
197
        contentHeight: parent.height
 
198
 
 
199
        Item {
 
200
            id: itemContainer
 
201
            //            color: "white"
 
202
            anchors {
 
203
                left: parent.left
 
204
                top: parent.top
 
205
            }
 
206
 
 
207
            width: childrenRect.width + units.gu(6)
 
208
            height: parent.height
 
209
        }
 
210
 
 
211
//        Behavior on contentX {
 
212
//            NumberAnimation {
 
213
//                duration: 600
 
214
//                easing.type: Easing.InOutQuart
 
215
//            }
 
216
//        }
 
217
 
 
218
        function scrollToStart() {
 
219
            contentX = 0
 
220
        }
 
221
    }
 
222
 
 
223
    ListModel
 
224
    {
 
225
        id: articleModel
 
226
    }
 
227
}