Merge lp:~osomon/webbrowser-app/newTabRefactoring-autopilot-tests into lp:webbrowser-app
- newTabRefactoring-autopilot-tests
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Olivier Tilloy |
Approved revision: | 914 |
Merged at revision: | 1039 |
Proposed branch: | lp:~osomon/webbrowser-app/newTabRefactoring-autopilot-tests |
Merge into: | lp:webbrowser-app |
Prerequisite: | lp:~rpadovani/webbrowser-app/newTabRefactoring |
Diff against target: |
2015 lines (+461/-1018) 23 files modified
src/app/webbrowser/Browser.qml (+7/-2) src/app/webbrowser/CMakeLists.txt (+0/-4) src/app/webbrowser/ExpandedHistoryView.qml (+12/-88) src/app/webbrowser/HistoryView.qml (+46/-147) src/app/webbrowser/NewTabView.qml (+32/-16) src/app/webbrowser/UrlDelegate.qml (+21/-5) src/app/webbrowser/UrlsList.qml (+13/-92) src/app/webbrowser/history-domainlist-chronological-model.cpp (+6/-1) src/app/webbrowser/history-domainlist-chronological-model.h (+4/-1) src/app/webbrowser/top-sites-model.cpp (+4/-0) src/app/webbrowser/top-sites-model.h (+2/-0) src/app/webbrowser/upstreamcomponents/ListItemWithActions.qml (+0/-372) src/app/webbrowser/upstreamcomponents/ListItemWithActionsCheckBox.qml (+0/-25) src/app/webbrowser/upstreamcomponents/MultipleSelectionListView.qml (+0/-199) src/app/webbrowser/upstreamcomponents/MultipleSelectionVisualModel.qml (+0/-31) src/app/webbrowser/upstreamcomponents/README (+0/-20) tests/autopilot/webbrowser_app/emulators/browser.py (+25/-7) tests/autopilot/webbrowser_app/tests/__init__.py (+1/-1) tests/autopilot/webbrowser_app/tests/test_new_tab_view.py (+274/-0) tests/autopilot/webbrowser_app/tests/test_private.py (+1/-1) tests/autopilot/webbrowser_app/tests/test_tabs.py (+3/-6) tests/unittests/history-domainlist-chronological-model/tst_HistoryDomainListChronologicalModelTests.cpp (+5/-0) tests/unittests/top-sites-model/tst_TopSitesModelTests.cpp (+5/-0) |
To merge this branch: | bzr merge lp:~osomon/webbrowser-app/newTabRefactoring-autopilot-tests |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Riccardo Padovani (community) | Approve | ||
Review via email: mp+260488@code.launchpad.net |
Commit message
Remove the upstreamcomponents folder, and use components from the UITK instead.
Add autopilot tests for the new tab view.
Description of the change
Riccardo Padovani (rpadovani) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:910
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Riccardo Padovani (rpadovani) wrote : | # |
Some things:
- onHistoryDomain
Why do you remove the possibilty to delete an entire domain? Anyway, if you are sure about this, you should also remove the swype - atm you can swype the domain, but then when you click on the trash icon nothing happens.
In the ExpandedHistoryView there isn't anymore the multiple selection - I think we should be consistent and have it (or don't have it) both in ExpandedHistoryView and HistoryView
- 911. By Olivier Tilloy
-
Fix embarassing regression…
Olivier Tilloy (osomon) wrote : | # |
> Some things:
>
> - onHistoryDomain
> browser.
>
> Why do you remove the possibilty to delete an entire domain? Anyway, if you
> are sure about this, you should also remove the swype - atm you can swype the
> domain, but then when you click on the trash icon nothing happens.
Good catch! This is something I overlooked, I just pushed a new revision that fixes it.
> In the ExpandedHistoryView there isn't anymore the multiple selection - I
> think we should be consistent and have it (or don't have it) both in
> ExpandedHistoryView and HistoryView
We never had multiple selection in the expanded history view, so this is not a regression. If you think we should have it, feel free to file a bug against webbrowser-app and ubuntu-ux to have design comment on the idea.
Riccardo Padovani (rpadovani) wrote : | # |
Looks good now then, thanks :-)
I opened bug #1459982 about multiple selection in history view
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:911
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 912. By Olivier Tilloy
-
Also emit countChanged() when the model has been reset.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:912
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 913. By Olivier Tilloy
-
Fix invalid indices in for loop.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:913
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 914. By Olivier Tilloy
-
When in multiple selection mode, clicking a domain (de)selects it instead of expanding it.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:914
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'src/app/webbrowser/Browser.qml' | |||
2 | --- src/app/webbrowser/Browser.qml 2015-06-02 14:24:15 +0000 | |||
3 | +++ src/app/webbrowser/Browser.qml 2015-06-02 14:24:15 +0000 | |||
4 | @@ -221,6 +221,7 @@ | |||
5 | 221 | NewTabView { | 221 | NewTabView { |
6 | 222 | historyModel: browser.historyModel | 222 | historyModel: browser.historyModel |
7 | 223 | bookmarksModel: browser.bookmarksModel | 223 | bookmarksModel: browser.bookmarksModel |
8 | 224 | settingsObject: settings | ||
9 | 224 | onBookmarkClicked: { | 225 | onBookmarkClicked: { |
10 | 225 | chrome.requestedUrl = url | 226 | chrome.requestedUrl = url |
11 | 226 | currentWebview.url = url | 227 | currentWebview.url = url |
12 | @@ -624,7 +625,6 @@ | |||
13 | 624 | var view = expandedHistoryViewComponent.createObject(historyViewContainer, {model: model}) | 625 | var view = expandedHistoryViewComponent.createObject(historyViewContainer, {model: model}) |
14 | 625 | view.onHistoryEntryClicked.connect(destroy) | 626 | view.onHistoryEntryClicked.connect(destroy) |
15 | 626 | } | 627 | } |
16 | 627 | onHistoryDomainRemoved: browser.historyModel.removeEntriesByDomain(domain) | ||
17 | 628 | onDone: destroy() | 628 | onDone: destroy() |
18 | 629 | } | 629 | } |
19 | 630 | } | 630 | } |
20 | @@ -639,7 +639,12 @@ | |||
21 | 639 | browser.openUrlInNewTab(url, true) | 639 | browser.openUrlInNewTab(url, true) |
22 | 640 | done() | 640 | done() |
23 | 641 | } | 641 | } |
25 | 642 | onHistoryEntryRemoved: browser.historyModel.removeEntryByUrl(url) | 642 | onHistoryEntryRemoved: { |
26 | 643 | if (count == 1) { | ||
27 | 644 | done() | ||
28 | 645 | } | ||
29 | 646 | browser.historyModel.removeEntryByUrl(url) | ||
30 | 647 | } | ||
31 | 643 | onDone: destroy() | 648 | onDone: destroy() |
32 | 644 | } | 649 | } |
33 | 645 | } | 650 | } |
34 | 646 | 651 | ||
35 | === modified file 'src/app/webbrowser/CMakeLists.txt' | |||
36 | --- src/app/webbrowser/CMakeLists.txt 2015-04-28 23:22:57 +0000 | |||
37 | +++ src/app/webbrowser/CMakeLists.txt 2015-06-02 14:24:15 +0000 | |||
38 | @@ -50,10 +50,6 @@ | |||
39 | 50 | DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app/webbrowser | 50 | DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app/webbrowser |
40 | 51 | FILES_MATCHING PATTERN *.png) | 51 | FILES_MATCHING PATTERN *.png) |
41 | 52 | 52 | ||
42 | 53 | install(DIRECTORY upstreamcomponents | ||
43 | 54 | DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app/webbrowser | ||
44 | 55 | FILES_MATCHING PATTERN *.qml) | ||
45 | 56 | |||
46 | 57 | install(DIRECTORY searchengines | 53 | install(DIRECTORY searchengines |
47 | 58 | DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app/webbrowser | 54 | DESTINATION ${CMAKE_INSTALL_DATADIR}/webbrowser-app/webbrowser |
48 | 59 | FILES_MATCHING PATTERN *.xml) | 55 | FILES_MATCHING PATTERN *.xml) |
49 | 60 | 56 | ||
50 | === modified file 'src/app/webbrowser/ExpandedHistoryView.qml' | |||
51 | --- src/app/webbrowser/ExpandedHistoryView.qml 2015-03-20 12:19:51 +0000 | |||
52 | +++ src/app/webbrowser/ExpandedHistoryView.qml 2015-06-02 14:24:15 +0000 | |||
53 | @@ -24,6 +24,7 @@ | |||
54 | 24 | id: expandedHistoryView | 24 | id: expandedHistoryView |
55 | 25 | 25 | ||
56 | 26 | property alias model: entriesListView.model | 26 | property alias model: entriesListView.model |
57 | 27 | property alias count: entriesListView.count | ||
58 | 27 | 28 | ||
59 | 28 | signal historyEntryClicked(url url) | 29 | signal historyEntryClicked(url url) |
60 | 29 | signal historyEntryRemoved(url url) | 30 | signal historyEntryRemoved(url url) |
61 | @@ -37,19 +38,22 @@ | |||
62 | 37 | ListView { | 38 | ListView { |
63 | 38 | id: entriesListView | 39 | id: entriesListView |
64 | 39 | 40 | ||
65 | 40 | property var _currentSwipedItem: null | ||
66 | 41 | |||
67 | 42 | anchors { | 41 | anchors { |
68 | 43 | top: header.bottom | 42 | top: header.bottom |
69 | 44 | bottom: parent.bottom | 43 | bottom: parent.bottom |
70 | 45 | left: parent.left | 44 | left: parent.left |
71 | 46 | right: parent.right | 45 | right: parent.right |
73 | 47 | margins: units.gu(2) | 46 | margins: units.gu(1.5) |
74 | 47 | leftMargin: 0 | ||
75 | 48 | } | 48 | } |
76 | 49 | 49 | ||
77 | 50 | section.property: "lastVisitDate" | 50 | section.property: "lastVisitDate" |
78 | 51 | section.delegate: HistorySectionDelegate { | 51 | section.delegate: HistorySectionDelegate { |
80 | 52 | width: parent.width | 52 | anchors { |
81 | 53 | left: parent.left | ||
82 | 54 | leftMargin: units.gu(1.5) | ||
83 | 55 | right: parent.right | ||
84 | 56 | } | ||
85 | 53 | } | 57 | } |
86 | 54 | 58 | ||
87 | 55 | delegate: UrlDelegate { | 59 | delegate: UrlDelegate { |
88 | @@ -61,91 +65,12 @@ | |||
89 | 61 | title: model.title | 65 | title: model.title |
90 | 62 | icon: model.icon | 66 | icon: model.icon |
91 | 63 | 67 | ||
173 | 64 | property var removalAnimation | 68 | onClicked: expandedHistoryView.historyEntryClicked(model.url) |
174 | 65 | function remove() { | 69 | onRemoved: expandedHistoryView.historyEntryRemoved(model.url) |
94 | 66 | removalAnimation.start() | ||
95 | 67 | } | ||
96 | 68 | |||
97 | 69 | onSwippingChanged: { | ||
98 | 70 | entriesListView._updateSwipeState(entriesDelegate) | ||
99 | 71 | } | ||
100 | 72 | |||
101 | 73 | onSwipeStateChanged: { | ||
102 | 74 | entriesListView._updateSwipeState(entriesDelegate) | ||
103 | 75 | } | ||
104 | 76 | |||
105 | 77 | leftSideAction: Action { | ||
106 | 78 | iconName: "delete" | ||
107 | 79 | text: i18n.tr("Delete") | ||
108 | 80 | onTriggered: { | ||
109 | 81 | entriesDelegate.remove() | ||
110 | 82 | } | ||
111 | 83 | } | ||
112 | 84 | |||
113 | 85 | ListView.onRemove: ScriptAction { | ||
114 | 86 | script: { | ||
115 | 87 | if (entriesListView._currentSwipedItem === entriesDelegate) { | ||
116 | 88 | entriesListView._currentSwipedItem = null | ||
117 | 89 | } | ||
118 | 90 | } | ||
119 | 91 | } | ||
120 | 92 | |||
121 | 93 | removalAnimation: SequentialAnimation { | ||
122 | 94 | alwaysRunToEnd: true | ||
123 | 95 | |||
124 | 96 | PropertyAction { | ||
125 | 97 | target: entriesDelegate | ||
126 | 98 | property: "ListView.delayRemove" | ||
127 | 99 | value: true | ||
128 | 100 | } | ||
129 | 101 | |||
130 | 102 | UbuntuNumberAnimation { | ||
131 | 103 | target: entriesDelegate | ||
132 | 104 | property: "height" | ||
133 | 105 | to: 0 | ||
134 | 106 | } | ||
135 | 107 | |||
136 | 108 | PropertyAction { | ||
137 | 109 | target: entriesDelegate | ||
138 | 110 | property: "ListView.delayRemove" | ||
139 | 111 | value: false | ||
140 | 112 | } | ||
141 | 113 | |||
142 | 114 | ScriptAction { | ||
143 | 115 | script: { | ||
144 | 116 | if(entriesListView.count === 1) { | ||
145 | 117 | expandedHistoryView.done() | ||
146 | 118 | } | ||
147 | 119 | historyEntryRemoved(model.url) | ||
148 | 120 | } | ||
149 | 121 | } | ||
150 | 122 | } | ||
151 | 123 | |||
152 | 124 | onItemClicked: { | ||
153 | 125 | historyEntryClicked(model.url) | ||
154 | 126 | } | ||
155 | 127 | } | ||
156 | 128 | |||
157 | 129 | function _updateSwipeState(item) { | ||
158 | 130 | if (item.swipping) { | ||
159 | 131 | return | ||
160 | 132 | } | ||
161 | 133 | |||
162 | 134 | if (item.swipeState !== "Normal") { | ||
163 | 135 | if (entriesListView._currentSwipedItem !== item) { | ||
164 | 136 | if (entriesListView._currentSwipedItem) { | ||
165 | 137 | entriesListView._currentSwipedItem.resetSwipe() | ||
166 | 138 | } | ||
167 | 139 | entriesListView._currentSwipedItem = item | ||
168 | 140 | } | ||
169 | 141 | } else if (item.swipeState !== "Normal" | ||
170 | 142 | && entriesListView._currentSwipedItem === item) { | ||
171 | 143 | entriesListView._currentSwipedItem = null | ||
172 | 144 | } | ||
175 | 145 | } | 70 | } |
176 | 146 | } | 71 | } |
177 | 147 | 72 | ||
179 | 148 | Rectangle { | 73 | Item { |
180 | 149 | id: header | 74 | id: header |
181 | 150 | 75 | ||
182 | 151 | anchors { | 76 | anchors { |
183 | @@ -155,8 +80,6 @@ | |||
184 | 155 | } | 80 | } |
185 | 156 | height: units.gu(8) | 81 | height: units.gu(8) |
186 | 157 | 82 | ||
187 | 158 | color: "#f6f6f6" | ||
188 | 159 | |||
189 | 160 | Rectangle { | 83 | Rectangle { |
190 | 161 | anchors { | 84 | anchors { |
191 | 162 | left: parent.left | 85 | left: parent.left |
192 | @@ -178,6 +101,7 @@ | |||
193 | 178 | icon: expandedHistoryView.model.lastVisitedIcon | 101 | icon: expandedHistoryView.model.lastVisitedIcon |
194 | 179 | title: expandedHistoryView.model.domain | 102 | title: expandedHistoryView.model.domain |
195 | 180 | url: i18n.tr("%1 page", "%1 pages", entriesListView.count).arg(entriesListView.count) | 103 | url: i18n.tr("%1 page", "%1 pages", entriesListView.count).arg(entriesListView.count) |
196 | 104 | enabled: false | ||
197 | 181 | } | 105 | } |
198 | 182 | 106 | ||
199 | 183 | Button { | 107 | Button { |
200 | 184 | 108 | ||
201 | === modified file 'src/app/webbrowser/HistoryView.qml' | |||
202 | --- src/app/webbrowser/HistoryView.qml 2015-03-20 12:19:51 +0000 | |||
203 | +++ src/app/webbrowser/HistoryView.qml 2015-06-02 14:24:15 +0000 | |||
204 | @@ -17,9 +17,8 @@ | |||
205 | 17 | */ | 17 | */ |
206 | 18 | 18 | ||
207 | 19 | import QtQuick 2.0 | 19 | import QtQuick 2.0 |
209 | 20 | import Ubuntu.Components 1.1 | 20 | import Ubuntu.Components 1.2 |
210 | 21 | import webbrowserapp.private 0.1 | 21 | import webbrowserapp.private 0.1 |
211 | 22 | import "upstreamcomponents" | ||
212 | 23 | 22 | ||
213 | 24 | Item { | 23 | Item { |
214 | 25 | id: historyView | 24 | id: historyView |
215 | @@ -27,7 +26,6 @@ | |||
216 | 27 | property alias historyModel: historyTimeframeModel.sourceModel | 26 | property alias historyModel: historyTimeframeModel.sourceModel |
217 | 28 | 27 | ||
218 | 29 | signal seeMoreEntriesClicked(var model) | 28 | signal seeMoreEntriesClicked(var model) |
219 | 30 | signal historyDomainRemoved(var domain) | ||
220 | 31 | signal done() | 29 | signal done() |
221 | 32 | 30 | ||
222 | 33 | Rectangle { | 31 | Rectangle { |
223 | @@ -35,37 +33,18 @@ | |||
224 | 35 | color: "#f6f6f6" | 33 | color: "#f6f6f6" |
225 | 36 | } | 34 | } |
226 | 37 | 35 | ||
244 | 38 | UbuntuNumberAnimation { | 36 | ListView { |
228 | 39 | id: topBarOpenAnimation | ||
229 | 40 | target: topBar | ||
230 | 41 | property: "height" | ||
231 | 42 | to: units.gu(5) | ||
232 | 43 | alwaysRunToEnd: true | ||
233 | 44 | } | ||
234 | 45 | |||
235 | 46 | UbuntuNumberAnimation { | ||
236 | 47 | id: topBarCloseAnimation | ||
237 | 48 | target: topBar | ||
238 | 49 | property: "height" | ||
239 | 50 | to: 0 | ||
240 | 51 | alwaysRunToEnd: true | ||
241 | 52 | } | ||
242 | 53 | |||
243 | 54 | MultipleSelectionListView { | ||
245 | 55 | id: domainsListView | 37 | id: domainsListView |
246 | 56 | 38 | ||
247 | 57 | property var _currentSwipedItem: null | ||
248 | 58 | |||
249 | 59 | anchors { | 39 | anchors { |
250 | 60 | top: topBar.bottom | 40 | top: topBar.bottom |
251 | 61 | left: parent.left | 41 | left: parent.left |
252 | 62 | right: parent.right | 42 | right: parent.right |
253 | 63 | bottom: toolbar.top | 43 | bottom: toolbar.top |
254 | 64 | topMargin: units.gu(-0.5) // topMargin 2 - firstSection.topMargin 2.5 | ||
255 | 65 | rightMargin: units.gu(2) | 44 | rightMargin: units.gu(2) |
256 | 66 | } | 45 | } |
257 | 67 | 46 | ||
259 | 68 | listModel: HistoryDomainListChronologicalModel { | 47 | model: HistoryDomainListChronologicalModel { |
260 | 69 | sourceModel: HistoryDomainListModel { | 48 | sourceModel: HistoryDomainListModel { |
261 | 70 | sourceModel: HistoryTimeframeModel { | 49 | sourceModel: HistoryTimeframeModel { |
262 | 71 | id: historyTimeframeModel | 50 | id: historyTimeframeModel |
263 | @@ -80,17 +59,7 @@ | |||
264 | 80 | anchors.leftMargin: units.gu(2) | 59 | anchors.leftMargin: units.gu(2) |
265 | 81 | } | 60 | } |
266 | 82 | 61 | ||
278 | 83 | onSelectionDone: { | 62 | delegate: UrlDelegate { |
268 | 84 | var domains = new Array(); | ||
269 | 85 | for (var i=0; i < items.count; i++) { | ||
270 | 86 | domains[i] = items.get(i).model.domain | ||
271 | 87 | } | ||
272 | 88 | for (var i=0; i < domains.length; i++) { | ||
273 | 89 | historyView.historyDomainRemoved(domains[i]) | ||
274 | 90 | } | ||
275 | 91 | } | ||
276 | 92 | |||
277 | 93 | listDelegate: UrlDelegate { | ||
279 | 94 | id: urlDelegate | 63 | id: urlDelegate |
280 | 95 | width: parent.width | 64 | width: parent.width |
281 | 96 | height: units.gu(5) | 65 | height: units.gu(5) |
282 | @@ -99,111 +68,19 @@ | |||
283 | 99 | url: lastVisitedTitle | 68 | url: lastVisitedTitle |
284 | 100 | icon: model.lastVisitedIcon | 69 | icon: model.lastVisitedIcon |
285 | 101 | 70 | ||
363 | 102 | property var removalAnimation | 71 | onClicked: { |
364 | 103 | function remove() { | 72 | if (selectMode) { |
365 | 104 | removalAnimation.start() | 73 | selected = !selected |
366 | 105 | } | 74 | } else { |
290 | 106 | |||
291 | 107 | anchors { | ||
292 | 108 | left: parent.left | ||
293 | 109 | // we need to move left the favicon to align the favicon to | ||
294 | 110 | // other elements. Favicon has a container bigger than it. | ||
295 | 111 | // units.gu(3) it's the size of the favicon container | ||
296 | 112 | // units.dp(16) it's the size of the favicon | ||
297 | 113 | // the favicon is hCentered in the container | ||
298 | 114 | leftMargin: selectionMode ? - (units.gu(3) - units.dp(16)) / 2 : 0 | ||
299 | 115 | } | ||
300 | 116 | |||
301 | 117 | selectionMode: domainsListView.isInSelectionMode | ||
302 | 118 | selected: domainsListView.isSelected(urlDelegate) | ||
303 | 119 | |||
304 | 120 | onSwippingChanged: { | ||
305 | 121 | domainsListView._updateSwipeState(urlDelegate) | ||
306 | 122 | } | ||
307 | 123 | |||
308 | 124 | onSwipeStateChanged: { | ||
309 | 125 | domainsListView._updateSwipeState(urlDelegate) | ||
310 | 126 | } | ||
311 | 127 | |||
312 | 128 | leftSideAction: Action { | ||
313 | 129 | iconName: "delete" | ||
314 | 130 | text: i18n.tr("Delete") | ||
315 | 131 | onTriggered: { | ||
316 | 132 | urlDelegate.remove() | ||
317 | 133 | } | ||
318 | 134 | } | ||
319 | 135 | |||
320 | 136 | ListView.onRemove: ScriptAction { | ||
321 | 137 | script: { | ||
322 | 138 | if (domainsListView._currentSwipedItem === urlDelegate) { | ||
323 | 139 | domainsListView._currentSwipedItem = null | ||
324 | 140 | } | ||
325 | 141 | } | ||
326 | 142 | } | ||
327 | 143 | |||
328 | 144 | removalAnimation: SequentialAnimation { | ||
329 | 145 | alwaysRunToEnd: true | ||
330 | 146 | |||
331 | 147 | PropertyAction { | ||
332 | 148 | target: urlDelegate | ||
333 | 149 | property: "ListView.delayRemove" | ||
334 | 150 | value: true | ||
335 | 151 | } | ||
336 | 152 | |||
337 | 153 | UbuntuNumberAnimation { | ||
338 | 154 | target: urlDelegate | ||
339 | 155 | property: "height" | ||
340 | 156 | to: 0 | ||
341 | 157 | } | ||
342 | 158 | |||
343 | 159 | PropertyAction { | ||
344 | 160 | target: urlDelegate | ||
345 | 161 | property: "ListView.delayRemove" | ||
346 | 162 | value: false | ||
347 | 163 | } | ||
348 | 164 | |||
349 | 165 | ScriptAction { | ||
350 | 166 | script: { | ||
351 | 167 | historyView.historyDomainRemoved(model.domain) | ||
352 | 168 | } | ||
353 | 169 | } | ||
354 | 170 | } | ||
355 | 171 | |||
356 | 172 | onItemClicked: { | ||
357 | 173 | if (domainsListView.isInSelectionMode) { | ||
358 | 174 | if (!domainsListView.selectItem(urlDelegate)) { | ||
359 | 175 | domainsListView.deselectItem(urlDelegate) | ||
360 | 176 | } | ||
361 | 177 | } | ||
362 | 178 | else { | ||
367 | 179 | historyView.seeMoreEntriesClicked(model.entries) | 75 | historyView.seeMoreEntriesClicked(model.entries) |
368 | 180 | } | 76 | } |
369 | 181 | } | 77 | } |
391 | 182 | onItemPressAndHold: { | 78 | onRemoved: historyView.historyModel.removeEntriesByDomain(model.domain) |
392 | 183 | domainsListView.startSelection() | 79 | onPressAndHold: { |
393 | 184 | domainsListView.selectItem(urlDelegate) | 80 | selectMode = !selectMode |
394 | 185 | } | 81 | if (selectMode) { |
395 | 186 | } | 82 | domainsListView.ViewItems.selectedIndices = [index] |
375 | 187 | |||
376 | 188 | /* | ||
377 | 189 | * Functions for manage swype and multiple selection together | ||
378 | 190 | * Developed upstream | ||
379 | 191 | */ | ||
380 | 192 | function _updateSwipeState(item) { | ||
381 | 193 | if (item.swipping) { | ||
382 | 194 | return | ||
383 | 195 | } | ||
384 | 196 | |||
385 | 197 | if (item.swipeState !== "Normal") { | ||
386 | 198 | if (domainsListView._currentSwipedItem !== item) { | ||
387 | 199 | if (domainsListView._currentSwipedItem) { | ||
388 | 200 | domainsListView._currentSwipedItem.resetSwipe() | ||
389 | 201 | } | ||
390 | 202 | domainsListView._currentSwipedItem = item | ||
396 | 203 | } | 83 | } |
397 | 204 | } else if (item.swipeState !== "Normal" | ||
398 | 205 | && domainsListView._currentSwipedItem === item) { | ||
399 | 206 | domainsListView._currentSwipedItem = null | ||
400 | 207 | } | 84 | } |
401 | 208 | } | 85 | } |
402 | 209 | } | 86 | } |
403 | @@ -253,11 +130,19 @@ | |||
404 | 253 | 130 | ||
405 | 254 | Item { | 131 | Item { |
406 | 255 | id: topBar | 132 | id: topBar |
412 | 256 | visible: domainsListView.isInSelectionMode | 133 | |
413 | 257 | 134 | visible: domainsListView.ViewItems.selectMode | |
414 | 258 | onVisibleChanged: visible ? topBarOpenAnimation.start() : topBarCloseAnimation.start() | 135 | height: visible ? units.gu(5) : 0 |
415 | 259 | 136 | ||
416 | 260 | anchors { left: parent.left; right: parent.right; top: parent.top } | 137 | Behavior on height { |
417 | 138 | UbuntuNumberAnimation {} | ||
418 | 139 | } | ||
419 | 140 | |||
420 | 141 | anchors { | ||
421 | 142 | left: parent.left | ||
422 | 143 | right: parent.right | ||
423 | 144 | top: parent.top | ||
424 | 145 | } | ||
425 | 261 | 146 | ||
426 | 262 | Rectangle { | 147 | Rectangle { |
427 | 263 | width: parent.width | 148 | width: parent.width |
428 | @@ -281,7 +166,7 @@ | |||
429 | 281 | 166 | ||
430 | 282 | MouseArea { | 167 | MouseArea { |
431 | 283 | anchors.fill: parent | 168 | anchors.fill: parent |
433 | 284 | onClicked: domainsListView.cancelSelection() | 169 | onClicked: domainsListView.ViewItems.selectMode = false |
434 | 285 | } | 170 | } |
435 | 286 | 171 | ||
436 | 287 | anchors.left: parent.left | 172 | anchors.left: parent.left |
437 | @@ -297,10 +182,14 @@ | |||
438 | 297 | MouseArea { | 182 | MouseArea { |
439 | 298 | anchors.fill: parent | 183 | anchors.fill: parent |
440 | 299 | onClicked: { | 184 | onClicked: { |
443 | 300 | if (domainsListView.selectedItems.count === domainsListView.count) { | 185 | if (domainsListView.ViewItems.selectedIndices.length === domainsListView.count) { |
444 | 301 | domainsListView.clearSelection() | 186 | domainsListView.ViewItems.selectedIndices = [] |
445 | 302 | } else { | 187 | } else { |
447 | 303 | domainsListView.selectAll() | 188 | var indices = [] |
448 | 189 | for (var i = 0; i < domainsListView.count; ++i) { | ||
449 | 190 | indices.push(i) | ||
450 | 191 | } | ||
451 | 192 | domainsListView.ViewItems.selectedIndices = indices | ||
452 | 304 | } | 193 | } |
453 | 305 | } | 194 | } |
454 | 306 | } | 195 | } |
455 | @@ -319,11 +208,21 @@ | |||
456 | 319 | 208 | ||
457 | 320 | iconName: "delete" | 209 | iconName: "delete" |
458 | 321 | text: i18n.tr("Delete") | 210 | text: i18n.tr("Delete") |
460 | 322 | enabled: domainsListView.selectedItems.count > 0 | 211 | enabled: domainsListView.ViewItems.selectedIndices.length > 0 |
461 | 323 | 212 | ||
462 | 324 | MouseArea { | 213 | MouseArea { |
463 | 325 | anchors.fill: parent | 214 | anchors.fill: parent |
465 | 326 | onClicked: domainsListView.endSelection() | 215 | onClicked: { |
466 | 216 | var indices = domainsListView.ViewItems.selectedIndices | ||
467 | 217 | var domains = [] | ||
468 | 218 | for (var i in indices) { | ||
469 | 219 | domains.push(domainsListView.model.get(indices[i])) | ||
470 | 220 | } | ||
471 | 221 | domainsListView.ViewItems.selectMode = false | ||
472 | 222 | for (var j in domains) { | ||
473 | 223 | historyModel.removeEntriesByDomain(domains[j]) | ||
474 | 224 | } | ||
475 | 225 | } | ||
476 | 327 | } | 226 | } |
477 | 328 | 227 | ||
478 | 329 | anchors.right: parent.right | 228 | anchors.right: parent.right |
479 | 330 | 229 | ||
480 | === modified file 'src/app/webbrowser/NewTabView.qml' | |||
481 | --- src/app/webbrowser/NewTabView.qml 2015-06-02 14:24:15 +0000 | |||
482 | +++ src/app/webbrowser/NewTabView.qml 2015-06-02 14:24:15 +0000 | |||
483 | @@ -17,8 +17,8 @@ | |||
484 | 17 | */ | 17 | */ |
485 | 18 | 18 | ||
486 | 19 | import QtQuick 2.0 | 19 | import QtQuick 2.0 |
489 | 20 | import Ubuntu.Components 1.1 | 20 | import Qt.labs.settings 1.0 |
490 | 21 | import Ubuntu.Components.ListItems 1.0 as ListItem | 21 | import Ubuntu.Components 1.2 |
491 | 22 | import webbrowserapp.private 0.1 | 22 | import webbrowserapp.private 0.1 |
492 | 23 | import ".." | 23 | import ".." |
493 | 24 | 24 | ||
494 | @@ -27,6 +27,7 @@ | |||
495 | 27 | 27 | ||
496 | 28 | property QtObject bookmarksModel | 28 | property QtObject bookmarksModel |
497 | 29 | property alias historyModel: historyTimeframeModel.sourceModel | 29 | property alias historyModel: historyTimeframeModel.sourceModel |
498 | 30 | property Settings settingsObject | ||
499 | 30 | 31 | ||
500 | 31 | signal bookmarkClicked(url url) | 32 | signal bookmarkClicked(url url) |
501 | 32 | signal bookmarkRemoved(url url) | 33 | signal bookmarkRemoved(url url) |
502 | @@ -45,7 +46,6 @@ | |||
503 | 45 | property bool seeMoreBookmarksView: bookmarksCountLimit > 4 | 46 | property bool seeMoreBookmarksView: bookmarksCountLimit > 4 |
504 | 46 | property int bookmarksCountLimit: Math.min(4, numberOfBookmarks) | 47 | property int bookmarksCountLimit: Math.min(4, numberOfBookmarks) |
505 | 47 | property int numberOfBookmarks: bookmarksModel ? bookmarksModel.count : 0 | 48 | property int numberOfBookmarks: bookmarksModel ? bookmarksModel.count : 0 |
506 | 48 | property int numberOfTopSites: historyModel ? historyModel.count : 0 | ||
507 | 49 | 49 | ||
508 | 50 | // Force the topsites section to reappear when remove a bookmark while | 50 | // Force the topsites section to reappear when remove a bookmark while |
509 | 51 | // the bookmarks list is expanded and there aren't anymore > 5 | 51 | // the bookmarks list is expanded and there aren't anymore > 5 |
510 | @@ -72,7 +72,6 @@ | |||
511 | 72 | id: contentColumn | 72 | id: contentColumn |
512 | 73 | anchors { | 73 | anchors { |
513 | 74 | left: parent.left | 74 | left: parent.left |
514 | 75 | leftMargin: units.gu(1.5) | ||
515 | 76 | right: parent.right | 75 | right: parent.right |
516 | 77 | rightMargin: units.gu(1.5) | 76 | rightMargin: units.gu(1.5) |
517 | 78 | } | 77 | } |
518 | @@ -80,7 +79,11 @@ | |||
519 | 80 | 79 | ||
520 | 81 | Row { | 80 | Row { |
521 | 82 | height: units.gu(6) | 81 | height: units.gu(6) |
523 | 83 | anchors { left: parent.left; right: parent.right } | 82 | anchors { |
524 | 83 | left: parent.left | ||
525 | 84 | leftMargin: units.gu(1.5) | ||
526 | 85 | right: parent.right | ||
527 | 86 | } | ||
528 | 84 | spacing: units.gu(1.5) | 87 | spacing: units.gu(1.5) |
529 | 85 | 88 | ||
530 | 86 | Icon { | 89 | Icon { |
531 | @@ -108,6 +111,7 @@ | |||
532 | 108 | 111 | ||
533 | 109 | Button { | 112 | Button { |
534 | 110 | id: moreButton | 113 | id: moreButton |
535 | 114 | objectName: "bookmarks.moreButton" | ||
536 | 111 | height: parent.height - units.gu(2) | 115 | height: parent.height - units.gu(2) |
537 | 112 | 116 | ||
538 | 113 | anchors { top: parent.top; topMargin: units.gu(1) } | 117 | anchors { top: parent.top; topMargin: units.gu(1) } |
539 | @@ -129,7 +133,11 @@ | |||
540 | 129 | 133 | ||
541 | 130 | Rectangle { | 134 | Rectangle { |
542 | 131 | height: units.gu(0.1) | 135 | height: units.gu(0.1) |
544 | 132 | anchors { left: parent.left; right: parent.right } | 136 | anchors { |
545 | 137 | left: parent.left | ||
546 | 138 | leftMargin: units.gu(1.5) | ||
547 | 139 | right: parent.right | ||
548 | 140 | } | ||
549 | 133 | color: "#d3d3d3" | 141 | color: "#d3d3d3" |
550 | 134 | } | 142 | } |
551 | 135 | 143 | ||
552 | @@ -137,7 +145,6 @@ | |||
553 | 137 | id: bookmarksColumn | 145 | id: bookmarksColumn |
554 | 138 | anchors { | 146 | anchors { |
555 | 139 | left: parent.left | 147 | left: parent.left |
556 | 140 | leftMargin: units.gu(-1.5) | ||
557 | 141 | right: parent.right | 148 | right: parent.right |
558 | 142 | } | 149 | } |
559 | 143 | 150 | ||
560 | @@ -147,7 +154,7 @@ | |||
561 | 147 | spacing: 0 | 154 | spacing: 0 |
562 | 148 | 155 | ||
563 | 149 | UrlDelegate { | 156 | UrlDelegate { |
565 | 150 | id: homepageBookmark | 157 | objectName: "homepageBookmark" |
566 | 151 | anchors { | 158 | anchors { |
567 | 152 | left: parent.left | 159 | left: parent.left |
568 | 153 | right: parent.right | 160 | right: parent.right |
569 | @@ -156,12 +163,14 @@ | |||
570 | 156 | 163 | ||
571 | 157 | title: i18n.tr('Homepage') | 164 | title: i18n.tr('Homepage') |
572 | 158 | 165 | ||
575 | 159 | url: settings.homepage | 166 | leadingActions: null |
576 | 160 | onItemClicked: newTabView.bookmarkClicked(url) | 167 | |
577 | 168 | url: newTabView.settingsObject.homepage | ||
578 | 169 | onClicked: newTabView.bookmarkClicked(url) | ||
579 | 161 | } | 170 | } |
580 | 162 | 171 | ||
581 | 163 | UrlsList { | 172 | UrlsList { |
583 | 164 | id: bookmarksList | 173 | objectName: "bookmarksList" |
584 | 165 | anchors { | 174 | anchors { |
585 | 166 | left: parent.left | 175 | left: parent.left |
586 | 167 | right: parent.right | 176 | right: parent.right |
587 | @@ -177,13 +186,13 @@ | |||
588 | 177 | } | 186 | } |
589 | 178 | } | 187 | } |
590 | 179 | 188 | ||
592 | 180 | Rectangle { | 189 | Item { |
593 | 181 | height: units.gu(6) | 190 | height: units.gu(6) |
594 | 182 | anchors { | 191 | anchors { |
595 | 183 | left: parent.left | 192 | left: parent.left |
596 | 193 | leftMargin: units.gu(1.5) | ||
597 | 184 | right: parent.right | 194 | right: parent.right |
598 | 185 | } | 195 | } |
599 | 186 | color: "#f6f6f6" | ||
600 | 187 | 196 | ||
601 | 188 | Label { | 197 | Label { |
602 | 189 | anchors { | 198 | anchors { |
603 | @@ -203,7 +212,11 @@ | |||
604 | 203 | 212 | ||
605 | 204 | Rectangle { | 213 | Rectangle { |
606 | 205 | height: units.gu(0.1) | 214 | height: units.gu(0.1) |
608 | 206 | anchors { left: parent.left; right: parent.right } | 215 | anchors { |
609 | 216 | left: parent.left | ||
610 | 217 | leftMargin: units.gu(1.5) | ||
611 | 218 | right: parent.right | ||
612 | 219 | } | ||
613 | 207 | color: "#d3d3d3" | 220 | color: "#d3d3d3" |
614 | 208 | 221 | ||
615 | 209 | opacity: internal.seeMoreBookmarksView ? 0.0 : 1.0 | 222 | opacity: internal.seeMoreBookmarksView ? 0.0 : 1.0 |
616 | @@ -211,12 +224,14 @@ | |||
617 | 211 | } | 224 | } |
618 | 212 | 225 | ||
619 | 213 | Label { | 226 | Label { |
620 | 227 | objectName: "notopsites" | ||
621 | 228 | |||
622 | 214 | height: units.gu(11) | 229 | height: units.gu(11) |
623 | 215 | anchors { | 230 | anchors { |
624 | 216 | left: parent.left | 231 | left: parent.left |
625 | 217 | right: parent.right | 232 | right: parent.right |
626 | 218 | } | 233 | } |
628 | 219 | visible: internal.numberOfTopSites === 0 | 234 | visible: topSitesModel.count == 0 |
629 | 220 | 235 | ||
630 | 221 | horizontalAlignment: Text.AlignHCenter | 236 | horizontalAlignment: Text.AlignHCenter |
631 | 222 | verticalAlignment: Text.AlignVCenter | 237 | verticalAlignment: Text.AlignVCenter |
632 | @@ -226,14 +241,15 @@ | |||
633 | 226 | } | 241 | } |
634 | 227 | 242 | ||
635 | 228 | UrlsList { | 243 | UrlsList { |
636 | 244 | objectName: "topSitesList" | ||
637 | 229 | anchors { | 245 | anchors { |
638 | 230 | left: parent.left | 246 | left: parent.left |
639 | 231 | leftMargin: units.gu(-1.5) | ||
640 | 232 | right: parent.right | 247 | right: parent.right |
641 | 233 | } | 248 | } |
642 | 234 | 249 | ||
643 | 235 | opacity: internal.seeMoreBookmarksView ? 0.0 : 1.0 | 250 | opacity: internal.seeMoreBookmarksView ? 0.0 : 1.0 |
644 | 236 | Behavior on opacity { UbuntuNumberAnimation {} } | 251 | Behavior on opacity { UbuntuNumberAnimation {} } |
645 | 252 | visible: opacity > 0 | ||
646 | 237 | 253 | ||
647 | 238 | limit: 10 | 254 | limit: 10 |
648 | 239 | spacing: 0 | 255 | spacing: 0 |
649 | 240 | 256 | ||
650 | === modified file 'src/app/webbrowser/UrlDelegate.qml' | |||
651 | --- src/app/webbrowser/UrlDelegate.qml 2015-03-20 12:19:51 +0000 | |||
652 | +++ src/app/webbrowser/UrlDelegate.qml 2015-06-02 14:24:15 +0000 | |||
653 | @@ -17,20 +17,26 @@ | |||
654 | 17 | */ | 17 | */ |
655 | 18 | 18 | ||
656 | 19 | import QtQuick 2.0 | 19 | import QtQuick 2.0 |
658 | 20 | import Ubuntu.Components 1.1 | 20 | import Ubuntu.Components 1.2 |
659 | 21 | import ".." | 21 | import ".." |
660 | 22 | import "upstreamcomponents" | ||
661 | 23 | 22 | ||
663 | 24 | ListItemWithActions { | 23 | ListItem { |
664 | 25 | id: urlDelegate | 24 | id: urlDelegate |
665 | 26 | 25 | ||
666 | 27 | property alias icon: icon.source | 26 | property alias icon: icon.source |
667 | 28 | property alias title: title.text | 27 | property alias title: title.text |
668 | 29 | property alias url: url.text | 28 | property alias url: url.text |
670 | 30 | color: "#f6f6f6" | 29 | |
671 | 30 | divider.visible: false | ||
672 | 31 | |||
673 | 32 | signal removed() | ||
674 | 31 | 33 | ||
675 | 32 | Row { | 34 | Row { |
677 | 33 | anchors.verticalCenter: parent.verticalCenter | 35 | anchors { |
678 | 36 | verticalCenter: parent.verticalCenter | ||
679 | 37 | left: parent.left | ||
680 | 38 | leftMargin: units.gu(1.5) | ||
681 | 39 | } | ||
682 | 34 | spacing: units.gu(1) | 40 | spacing: units.gu(1) |
683 | 35 | 41 | ||
684 | 36 | UbuntuShape { | 42 | UbuntuShape { |
685 | @@ -69,4 +75,14 @@ | |||
686 | 69 | } | 75 | } |
687 | 70 | } | 76 | } |
688 | 71 | } | 77 | } |
689 | 78 | |||
690 | 79 | leadingActions: ListItemActions { | ||
691 | 80 | actions: [ | ||
692 | 81 | Action { | ||
693 | 82 | objectName: "leadingAction.delete" | ||
694 | 83 | iconName: "delete" | ||
695 | 84 | onTriggered: urlDelegate.removed() | ||
696 | 85 | } | ||
697 | 86 | ] | ||
698 | 87 | } | ||
699 | 72 | } | 88 | } |
700 | 73 | 89 | ||
701 | === modified file 'src/app/webbrowser/UrlsList.qml' | |||
702 | --- src/app/webbrowser/UrlsList.qml 2015-06-02 14:24:15 +0000 | |||
703 | +++ src/app/webbrowser/UrlsList.qml 2015-06-02 14:24:15 +0000 | |||
704 | @@ -21,6 +21,7 @@ | |||
705 | 21 | 21 | ||
706 | 22 | Column { | 22 | Column { |
707 | 23 | id: urlsList | 23 | id: urlsList |
708 | 24 | |||
709 | 24 | property alias model: urlsListRepeater.model | 25 | property alias model: urlsListRepeater.model |
710 | 25 | property int limit | 26 | property int limit |
711 | 26 | 27 | ||
712 | @@ -29,102 +30,22 @@ | |||
713 | 29 | 30 | ||
714 | 30 | spacing: units.gu(1) | 31 | spacing: units.gu(1) |
715 | 31 | 32 | ||
716 | 32 | move: Transition { UbuntuNumberAnimation { properties: "x, y" } } | ||
717 | 33 | |||
718 | 34 | Repeater { | 33 | Repeater { |
719 | 35 | id: urlsListRepeater | 34 | id: urlsListRepeater |
720 | 36 | property var _currentSwipedItem: null | ||
721 | 37 | 35 | ||
722 | 38 | delegate: Loader { | 36 | delegate: Loader { |
812 | 39 | sourceComponent: (index < limit) ? realDelegate : undefined | 37 | active: index < limit |
813 | 40 | Component { | 38 | sourceComponent: UrlDelegate{ |
814 | 41 | id: realDelegate | 39 | id: urlDelegate |
815 | 42 | UrlDelegate{ | 40 | width: urlsList.width |
816 | 43 | id: urlDelegate | 41 | height: units.gu(5) |
817 | 44 | width: urlsList.width | 42 | |
818 | 45 | height: units.gu(5) | 43 | icon: model.icon |
819 | 46 | 44 | title: model.title ? model.title : model.url | |
820 | 47 | icon: model.icon | 45 | url: model.url |
821 | 48 | title: model.title ? model.title : model.url | 46 | |
822 | 49 | url: model.url | 47 | onClicked: urlsList.urlClicked(model.url) |
823 | 50 | 48 | onRemoved: urlsList.urlRemoved(model.url) | |
735 | 51 | onItemClicked: urlClicked(model.url) | ||
736 | 52 | |||
737 | 53 | property var removalAnimation | ||
738 | 54 | function remove() { | ||
739 | 55 | removalAnimation.start() | ||
740 | 56 | } | ||
741 | 57 | |||
742 | 58 | onSwippingChanged: { | ||
743 | 59 | urlsListRepeater._updateSwipeState(urlDelegate) | ||
744 | 60 | } | ||
745 | 61 | |||
746 | 62 | onSwipeStateChanged: { | ||
747 | 63 | urlsListRepeater._updateSwipeState(urlDelegate) | ||
748 | 64 | } | ||
749 | 65 | |||
750 | 66 | leftSideAction: Action { | ||
751 | 67 | iconName: "delete" | ||
752 | 68 | text: i18n.tr("Delete") | ||
753 | 69 | onTriggered: { | ||
754 | 70 | urlDelegate.remove() | ||
755 | 71 | } | ||
756 | 72 | } | ||
757 | 73 | |||
758 | 74 | ListView.onRemove: ScriptAction { | ||
759 | 75 | script: { | ||
760 | 76 | if (urlsListRepeater._currentSwipedItem === urlDelegate) { | ||
761 | 77 | urlsListRepeater._currentSwipedItem = null | ||
762 | 78 | } | ||
763 | 79 | } | ||
764 | 80 | } | ||
765 | 81 | |||
766 | 82 | removalAnimation: SequentialAnimation { | ||
767 | 83 | alwaysRunToEnd: true | ||
768 | 84 | |||
769 | 85 | PropertyAction { | ||
770 | 86 | target: urlDelegate | ||
771 | 87 | property: "ListView.delayRemove" | ||
772 | 88 | value: true | ||
773 | 89 | } | ||
774 | 90 | |||
775 | 91 | UbuntuNumberAnimation { | ||
776 | 92 | target: urlDelegate | ||
777 | 93 | property: "height" | ||
778 | 94 | to: 0 | ||
779 | 95 | } | ||
780 | 96 | |||
781 | 97 | PropertyAction { | ||
782 | 98 | target: urlDelegate | ||
783 | 99 | property: "ListView.delayRemove" | ||
784 | 100 | value: false | ||
785 | 101 | } | ||
786 | 102 | |||
787 | 103 | ScriptAction { | ||
788 | 104 | script: { | ||
789 | 105 | urlRemoved(model.url) | ||
790 | 106 | } | ||
791 | 107 | } | ||
792 | 108 | } | ||
793 | 109 | } | ||
794 | 110 | } | ||
795 | 111 | } | ||
796 | 112 | |||
797 | 113 | function _updateSwipeState(item) { | ||
798 | 114 | if (item.swipping) { | ||
799 | 115 | return | ||
800 | 116 | } | ||
801 | 117 | |||
802 | 118 | if (item.swipeState !== "Normal") { | ||
803 | 119 | if (urlsListRepeater._currentSwipedItem !== item) { | ||
804 | 120 | if (urlsListRepeater._currentSwipedItem) { | ||
805 | 121 | urlsListRepeater._currentSwipedItem.resetSwipe() | ||
806 | 122 | } | ||
807 | 123 | urlsListRepeater._currentSwipedItem = item | ||
808 | 124 | } | ||
809 | 125 | } else if (item.swipeState !== "Normal" | ||
810 | 126 | && urlsListRepeater._currentSwipedItem === item) { | ||
811 | 127 | urlsListRepeater._currentSwipedItem = null | ||
824 | 128 | } | 49 | } |
825 | 129 | } | 50 | } |
826 | 130 | } | 51 | } |
827 | 131 | 52 | ||
828 | === modified file 'src/app/webbrowser/history-domainlist-chronological-model.cpp' | |||
829 | --- src/app/webbrowser/history-domainlist-chronological-model.cpp 2014-03-12 10:59:54 +0000 | |||
830 | +++ src/app/webbrowser/history-domainlist-chronological-model.cpp 2015-06-02 14:24:15 +0000 | |||
831 | @@ -1,5 +1,5 @@ | |||
832 | 1 | /* | 1 | /* |
834 | 2 | * Copyright 2013 Canonical Ltd. | 2 | * Copyright 2013-2015 Canonical Ltd. |
835 | 3 | * | 3 | * |
836 | 4 | * This file is part of webbrowser-app. | 4 | * This file is part of webbrowser-app. |
837 | 5 | * | 5 | * |
838 | @@ -48,3 +48,8 @@ | |||
839 | 48 | Q_EMIT sourceModelChanged(); | 48 | Q_EMIT sourceModelChanged(); |
840 | 49 | } | 49 | } |
841 | 50 | } | 50 | } |
842 | 51 | |||
843 | 52 | QString HistoryDomainListChronologicalModel::get(int index) const | ||
844 | 53 | { | ||
845 | 54 | return data(this->index(index, 0), HistoryDomainListModel::Domain).toString(); | ||
846 | 55 | } | ||
847 | 51 | 56 | ||
848 | === modified file 'src/app/webbrowser/history-domainlist-chronological-model.h' | |||
849 | --- src/app/webbrowser/history-domainlist-chronological-model.h 2014-03-12 10:59:54 +0000 | |||
850 | +++ src/app/webbrowser/history-domainlist-chronological-model.h 2015-06-02 14:24:15 +0000 | |||
851 | @@ -1,5 +1,5 @@ | |||
852 | 1 | /* | 1 | /* |
854 | 2 | * Copyright 2013 Canonical Ltd. | 2 | * Copyright 2013-2015 Canonical Ltd. |
855 | 3 | * | 3 | * |
856 | 4 | * This file is part of webbrowser-app. | 4 | * This file is part of webbrowser-app. |
857 | 5 | * | 5 | * |
858 | @@ -21,6 +21,7 @@ | |||
859 | 21 | 21 | ||
860 | 22 | // Qt | 22 | // Qt |
861 | 23 | #include <QtCore/QSortFilterProxyModel> | 23 | #include <QtCore/QSortFilterProxyModel> |
862 | 24 | #include <QtCore/QString> | ||
863 | 24 | 25 | ||
864 | 25 | class HistoryDomainListModel; | 26 | class HistoryDomainListModel; |
865 | 26 | 27 | ||
866 | @@ -36,6 +37,8 @@ | |||
867 | 36 | HistoryDomainListModel* sourceModel() const; | 37 | HistoryDomainListModel* sourceModel() const; |
868 | 37 | void setSourceModel(HistoryDomainListModel* sourceModel); | 38 | void setSourceModel(HistoryDomainListModel* sourceModel); |
869 | 38 | 39 | ||
870 | 40 | Q_INVOKABLE QString get(int index) const; | ||
871 | 41 | |||
872 | 39 | Q_SIGNALS: | 42 | Q_SIGNALS: |
873 | 40 | void sourceModelChanged() const; | 43 | void sourceModelChanged() const; |
874 | 41 | }; | 44 | }; |
875 | 42 | 45 | ||
876 | === modified file 'src/app/webbrowser/top-sites-model.cpp' | |||
877 | --- src/app/webbrowser/top-sites-model.cpp 2015-04-09 14:55:14 +0000 | |||
878 | +++ src/app/webbrowser/top-sites-model.cpp 2015-06-02 14:24:15 +0000 | |||
879 | @@ -34,6 +34,9 @@ | |||
880 | 34 | setDynamicSortFilter(true); | 34 | setDynamicSortFilter(true); |
881 | 35 | setSortRole(HistoryModel::Visits); | 35 | setSortRole(HistoryModel::Visits); |
882 | 36 | sort(0, Qt::DescendingOrder); | 36 | sort(0, Qt::DescendingOrder); |
883 | 37 | connect(this, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SIGNAL(countChanged())); | ||
884 | 38 | connect(this, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SIGNAL(countChanged())); | ||
885 | 39 | connect(this, SIGNAL(modelReset()), SIGNAL(countChanged())); | ||
886 | 37 | } | 40 | } |
887 | 38 | 41 | ||
888 | 39 | HistoryTimeframeModel* TopSitesModel::sourceModel() const | 42 | HistoryTimeframeModel* TopSitesModel::sourceModel() const |
889 | @@ -46,6 +49,7 @@ | |||
890 | 46 | if (sourceModel != this->sourceModel()) { | 49 | if (sourceModel != this->sourceModel()) { |
891 | 47 | QSortFilterProxyModel::setSourceModel(sourceModel); | 50 | QSortFilterProxyModel::setSourceModel(sourceModel); |
892 | 48 | Q_EMIT sourceModelChanged(); | 51 | Q_EMIT sourceModelChanged(); |
893 | 52 | Q_EMIT countChanged(); | ||
894 | 49 | } | 53 | } |
895 | 50 | } | 54 | } |
896 | 51 | 55 | ||
897 | 52 | 56 | ||
898 | === modified file 'src/app/webbrowser/top-sites-model.h' | |||
899 | --- src/app/webbrowser/top-sites-model.h 2015-04-08 22:17:08 +0000 | |||
900 | +++ src/app/webbrowser/top-sites-model.h 2015-06-02 14:24:15 +0000 | |||
901 | @@ -29,6 +29,7 @@ | |||
902 | 29 | Q_OBJECT | 29 | Q_OBJECT |
903 | 30 | 30 | ||
904 | 31 | Q_PROPERTY(HistoryTimeframeModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged) | 31 | Q_PROPERTY(HistoryTimeframeModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged) |
905 | 32 | Q_PROPERTY(int count READ rowCount NOTIFY countChanged) | ||
906 | 32 | 33 | ||
907 | 33 | public: | 34 | public: |
908 | 34 | TopSitesModel(QObject* parent=0); | 35 | TopSitesModel(QObject* parent=0); |
909 | @@ -38,6 +39,7 @@ | |||
910 | 38 | 39 | ||
911 | 39 | Q_SIGNALS: | 40 | Q_SIGNALS: |
912 | 40 | void sourceModelChanged() const; | 41 | void sourceModelChanged() const; |
913 | 42 | void countChanged() const; | ||
914 | 41 | 43 | ||
915 | 42 | protected: | 44 | protected: |
916 | 43 | // reimplemented from QSortFilterProxyModel | 45 | // reimplemented from QSortFilterProxyModel |
917 | 44 | 46 | ||
918 | === removed directory 'src/app/webbrowser/upstreamcomponents' | |||
919 | === removed file 'src/app/webbrowser/upstreamcomponents/ListItemWithActions.qml' | |||
920 | --- src/app/webbrowser/upstreamcomponents/ListItemWithActions.qml 2015-02-03 18:32:52 +0000 | |||
921 | +++ src/app/webbrowser/upstreamcomponents/ListItemWithActions.qml 1970-01-01 00:00:00 +0000 | |||
922 | @@ -1,372 +0,0 @@ | |||
923 | 1 | /* | ||
924 | 2 | * Copyright (C) 2012-2014 Canonical, Ltd. | ||
925 | 3 | * | ||
926 | 4 | * This program is free software; you can redistribute it and/or modify | ||
927 | 5 | * it under the terms of the GNU General Public License as published by | ||
928 | 6 | * the Free Software Foundation; version 3. | ||
929 | 7 | * | ||
930 | 8 | * This program is distributed in the hope that it will be useful, | ||
931 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
932 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
933 | 11 | * GNU General Public License for more details. | ||
934 | 12 | * | ||
935 | 13 | * You should have received a copy of the GNU General Public License | ||
936 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
937 | 15 | */ | ||
938 | 16 | |||
939 | 17 | import QtQuick 2.0 | ||
940 | 18 | import Ubuntu.Components 1.1 | ||
941 | 19 | |||
942 | 20 | Item { | ||
943 | 21 | id: root | ||
944 | 22 | |||
945 | 23 | property Action leftSideAction: null | ||
946 | 24 | property list<Action> rightSideActions | ||
947 | 25 | property double defaultHeight: units.gu(8) | ||
948 | 26 | property bool locked: false | ||
949 | 27 | property Action activeAction: null | ||
950 | 28 | property var activeItem: null | ||
951 | 29 | property bool triggerActionOnMouseRelease: false | ||
952 | 30 | property color color: Theme.palette.normal.background | ||
953 | 31 | property color selectedColor: "#E6E6E6" | ||
954 | 32 | property bool selected: false | ||
955 | 33 | property bool selectionMode: false | ||
956 | 34 | property alias internalAnchors: mainContents.anchors | ||
957 | 35 | default property alias contents: mainContents.children | ||
958 | 36 | |||
959 | 37 | readonly property double actionWidth: units.gu(5) | ||
960 | 38 | readonly property double leftActionWidth: units.gu(10) | ||
961 | 39 | readonly property double actionThreshold: actionWidth * 0.4 | ||
962 | 40 | readonly property double threshold: 0.4 | ||
963 | 41 | readonly property string swipeState: main.x == 0 ? "Normal" : main.x > 0 ? "LeftToRight" : "RightToLeft" | ||
964 | 42 | readonly property alias swipping: mainItemMoving.running | ||
965 | 43 | |||
966 | 44 | signal itemClicked(var mouse) | ||
967 | 45 | signal itemPressAndHold(var mouse) | ||
968 | 46 | |||
969 | 47 | function returnToBoundsRTL() | ||
970 | 48 | { | ||
971 | 49 | var actionFullWidth = actionWidth + units.gu(2) | ||
972 | 50 | var xOffset = Math.abs(main.x) | ||
973 | 51 | var index = Math.min(Math.floor(xOffset / actionFullWidth), rightSideActions.length) | ||
974 | 52 | |||
975 | 53 | if (index < 1) { | ||
976 | 54 | main.x = 0 | ||
977 | 55 | } else if (index === rightSideActions.length) { | ||
978 | 56 | main.x = -rightActionsView.width | ||
979 | 57 | } else { | ||
980 | 58 | main.x = -(actionFullWidth * index) | ||
981 | 59 | } | ||
982 | 60 | } | ||
983 | 61 | |||
984 | 62 | function returnToBoundsLTR() | ||
985 | 63 | { | ||
986 | 64 | var finalX = leftActionWidth | ||
987 | 65 | if (main.x > (finalX * root.threshold)) | ||
988 | 66 | main.x = finalX | ||
989 | 67 | else | ||
990 | 68 | main.x = 0 | ||
991 | 69 | } | ||
992 | 70 | |||
993 | 71 | function returnToBounds() | ||
994 | 72 | { | ||
995 | 73 | if (main.x < 0) { | ||
996 | 74 | returnToBoundsRTL() | ||
997 | 75 | } else if (main.x > 0) { | ||
998 | 76 | returnToBoundsLTR() | ||
999 | 77 | } | ||
1000 | 78 | } | ||
1001 | 79 | |||
1002 | 80 | function contains(item, point) | ||
1003 | 81 | { | ||
1004 | 82 | return (point.x >= item.x) && (point.x <= (item.x + item.width)) && (point.y >= item.y) && (point.y <= (item.y + item.height)); | ||
1005 | 83 | } | ||
1006 | 84 | |||
1007 | 85 | function getActionAt(point) | ||
1008 | 86 | { | ||
1009 | 87 | if (contains(leftActionView, point)) { | ||
1010 | 88 | return leftSideAction | ||
1011 | 89 | } else if (contains(rightActionsView, point)) { | ||
1012 | 90 | var newPoint = root.mapToItem(rightActionsView, point.x, point.y) | ||
1013 | 91 | for (var i = 0; i < rightActionsRepeater.count; i++) { | ||
1014 | 92 | var child = rightActionsRepeater.itemAt(i) | ||
1015 | 93 | if (contains(child, newPoint)) { | ||
1016 | 94 | return i | ||
1017 | 95 | } | ||
1018 | 96 | } | ||
1019 | 97 | } | ||
1020 | 98 | return -1 | ||
1021 | 99 | } | ||
1022 | 100 | |||
1023 | 101 | function updateActiveAction() | ||
1024 | 102 | { | ||
1025 | 103 | if ((main.x <= -root.actionWidth) && | ||
1026 | 104 | (main.x > -rightActionsView.width)) { | ||
1027 | 105 | var actionFullWidth = actionWidth + units.gu(2) | ||
1028 | 106 | var xOffset = Math.abs(main.x) | ||
1029 | 107 | var index = Math.min(Math.floor(xOffset / actionFullWidth), rightSideActions.length) | ||
1030 | 108 | index = index - 1 | ||
1031 | 109 | if (index > -1) { | ||
1032 | 110 | root.activeItem = rightActionsRepeater.itemAt(index) | ||
1033 | 111 | root.activeAction = root.rightSideActions[index] | ||
1034 | 112 | } | ||
1035 | 113 | } else { | ||
1036 | 114 | root.activeAction = null | ||
1037 | 115 | } | ||
1038 | 116 | } | ||
1039 | 117 | |||
1040 | 118 | function resetSwipe() | ||
1041 | 119 | { | ||
1042 | 120 | main.x = 0 | ||
1043 | 121 | } | ||
1044 | 122 | |||
1045 | 123 | states: [ | ||
1046 | 124 | State { | ||
1047 | 125 | name: "select" | ||
1048 | 126 | when: selectionMode || selected | ||
1049 | 127 | PropertyChanges { | ||
1050 | 128 | target: selectionIcon | ||
1051 | 129 | source: Qt.resolvedUrl("ListItemWithActionsCheckBox.qml") | ||
1052 | 130 | anchors.leftMargin: units.gu(2) | ||
1053 | 131 | } | ||
1054 | 132 | PropertyChanges { | ||
1055 | 133 | target: root | ||
1056 | 134 | locked: true | ||
1057 | 135 | } | ||
1058 | 136 | PropertyChanges { | ||
1059 | 137 | target: main | ||
1060 | 138 | x: 0 | ||
1061 | 139 | } | ||
1062 | 140 | } | ||
1063 | 141 | ] | ||
1064 | 142 | |||
1065 | 143 | height: defaultHeight | ||
1066 | 144 | clip: height !== defaultHeight | ||
1067 | 145 | |||
1068 | 146 | Rectangle { | ||
1069 | 147 | id: leftActionView | ||
1070 | 148 | |||
1071 | 149 | anchors { | ||
1072 | 150 | top: parent.top | ||
1073 | 151 | bottom: parent.bottom | ||
1074 | 152 | right: main.left | ||
1075 | 153 | } | ||
1076 | 154 | width: root.leftActionWidth + actionThreshold | ||
1077 | 155 | visible: leftSideAction | ||
1078 | 156 | color: "red" | ||
1079 | 157 | |||
1080 | 158 | Icon { | ||
1081 | 159 | anchors { | ||
1082 | 160 | centerIn: parent | ||
1083 | 161 | horizontalCenterOffset: actionThreshold / 2 | ||
1084 | 162 | } | ||
1085 | 163 | name: leftSideAction ? leftSideAction.iconName : "" | ||
1086 | 164 | color: Theme.palette.selected.field | ||
1087 | 165 | height: units.gu(3) | ||
1088 | 166 | width: units.gu(3) | ||
1089 | 167 | } | ||
1090 | 168 | } | ||
1091 | 169 | |||
1092 | 170 | Item { | ||
1093 | 171 | id: rightActionsView | ||
1094 | 172 | |||
1095 | 173 | anchors { | ||
1096 | 174 | top: main.top | ||
1097 | 175 | left: main.right | ||
1098 | 176 | leftMargin: units.gu(1) | ||
1099 | 177 | bottom: main.bottom | ||
1100 | 178 | } | ||
1101 | 179 | visible: rightSideActions.length > 0 | ||
1102 | 180 | width: rightActionsRepeater.count > 0 ? rightActionsRepeater.count * (root.actionWidth + units.gu(2)) + actionThreshold : 0 | ||
1103 | 181 | Row { | ||
1104 | 182 | anchors.fill: parent | ||
1105 | 183 | spacing: units.gu(2) | ||
1106 | 184 | Repeater { | ||
1107 | 185 | id: rightActionsRepeater | ||
1108 | 186 | |||
1109 | 187 | model: rightSideActions | ||
1110 | 188 | Item { | ||
1111 | 189 | property alias image: img | ||
1112 | 190 | |||
1113 | 191 | anchors { | ||
1114 | 192 | top: parent.top | ||
1115 | 193 | bottom: parent.bottom | ||
1116 | 194 | } | ||
1117 | 195 | width: root.actionWidth | ||
1118 | 196 | |||
1119 | 197 | Icon { | ||
1120 | 198 | id: img | ||
1121 | 199 | |||
1122 | 200 | anchors.centerIn: parent | ||
1123 | 201 | width: units.gu(3) | ||
1124 | 202 | height: units.gu(3) | ||
1125 | 203 | name: iconName | ||
1126 | 204 | color: root.activeAction === modelData || !root.triggerActionOnMouseRelease ? UbuntuColors.lightAubergine : Theme.palette.selected.background | ||
1127 | 205 | } | ||
1128 | 206 | } | ||
1129 | 207 | } | ||
1130 | 208 | } | ||
1131 | 209 | } | ||
1132 | 210 | |||
1133 | 211 | |||
1134 | 212 | Rectangle { | ||
1135 | 213 | id: main | ||
1136 | 214 | objectName: "mainItem" | ||
1137 | 215 | |||
1138 | 216 | anchors { | ||
1139 | 217 | top: parent.top | ||
1140 | 218 | bottom: parent.bottom | ||
1141 | 219 | } | ||
1142 | 220 | |||
1143 | 221 | width: parent.width | ||
1144 | 222 | color: mouseArea.pressed && swipeState === "Normal" ? root.selectedColor : root.color | ||
1145 | 223 | |||
1146 | 224 | Loader { | ||
1147 | 225 | id: selectionIcon | ||
1148 | 226 | |||
1149 | 227 | anchors { | ||
1150 | 228 | left: main.left | ||
1151 | 229 | verticalCenter: main.verticalCenter | ||
1152 | 230 | } | ||
1153 | 231 | width: (status === Loader.Ready) ? item.implicitWidth : 0 | ||
1154 | 232 | visible: (status === Loader.Ready) && (item.width === item.implicitWidth) | ||
1155 | 233 | Behavior on width { | ||
1156 | 234 | NumberAnimation { | ||
1157 | 235 | duration: UbuntuAnimation.SnapDuration | ||
1158 | 236 | } | ||
1159 | 237 | } | ||
1160 | 238 | } | ||
1161 | 239 | |||
1162 | 240 | |||
1163 | 241 | Item { | ||
1164 | 242 | id: mainContents | ||
1165 | 243 | |||
1166 | 244 | anchors { | ||
1167 | 245 | left: selectionIcon.right | ||
1168 | 246 | leftMargin: units.gu(2) | ||
1169 | 247 | top: parent.top | ||
1170 | 248 | topMargin: units.gu(1) | ||
1171 | 249 | right: parent.right | ||
1172 | 250 | rightMargin: units.gu(2) | ||
1173 | 251 | bottom: parent.bottom | ||
1174 | 252 | bottomMargin: units.gu(1) | ||
1175 | 253 | } | ||
1176 | 254 | } | ||
1177 | 255 | |||
1178 | 256 | Behavior on x { | ||
1179 | 257 | UbuntuNumberAnimation { | ||
1180 | 258 | id: mainItemMoving | ||
1181 | 259 | |||
1182 | 260 | easing.type: Easing.OutElastic | ||
1183 | 261 | duration: UbuntuAnimation.SlowDuration | ||
1184 | 262 | } | ||
1185 | 263 | } | ||
1186 | 264 | Behavior on color { | ||
1187 | 265 | enabled: (root.color != root.selectedColor) | ||
1188 | 266 | ColorAnimation {} | ||
1189 | 267 | } | ||
1190 | 268 | } | ||
1191 | 269 | |||
1192 | 270 | SequentialAnimation { | ||
1193 | 271 | id: triggerAction | ||
1194 | 272 | |||
1195 | 273 | property var currentItem: root.activeItem ? root.activeItem.image : null | ||
1196 | 274 | |||
1197 | 275 | running: false | ||
1198 | 276 | ParallelAnimation { | ||
1199 | 277 | UbuntuNumberAnimation { | ||
1200 | 278 | target: triggerAction.currentItem | ||
1201 | 279 | property: "opacity" | ||
1202 | 280 | from: 1.0 | ||
1203 | 281 | to: 0.0 | ||
1204 | 282 | duration: UbuntuAnimation.SlowDuration | ||
1205 | 283 | easing {type: Easing.InOutBack; } | ||
1206 | 284 | } | ||
1207 | 285 | UbuntuNumberAnimation { | ||
1208 | 286 | target: triggerAction.currentItem | ||
1209 | 287 | properties: "width, height" | ||
1210 | 288 | from: units.gu(3) | ||
1211 | 289 | to: root.actionWidth | ||
1212 | 290 | duration: UbuntuAnimation.SlowDuration | ||
1213 | 291 | easing {type: Easing.InOutBack; } | ||
1214 | 292 | } | ||
1215 | 293 | } | ||
1216 | 294 | PropertyAction { | ||
1217 | 295 | target: triggerAction.currentItem | ||
1218 | 296 | properties: "width, height" | ||
1219 | 297 | value: units.gu(3) | ||
1220 | 298 | } | ||
1221 | 299 | PropertyAction { | ||
1222 | 300 | target: triggerAction.currentItem | ||
1223 | 301 | properties: "opacity" | ||
1224 | 302 | value: 1.0 | ||
1225 | 303 | } | ||
1226 | 304 | ScriptAction { | ||
1227 | 305 | script: root.activeAction.triggered(root) | ||
1228 | 306 | } | ||
1229 | 307 | PauseAnimation { | ||
1230 | 308 | duration: 500 | ||
1231 | 309 | } | ||
1232 | 310 | UbuntuNumberAnimation { | ||
1233 | 311 | target: main | ||
1234 | 312 | property: "x" | ||
1235 | 313 | to: 0 | ||
1236 | 314 | |||
1237 | 315 | } | ||
1238 | 316 | } | ||
1239 | 317 | |||
1240 | 318 | MouseArea { | ||
1241 | 319 | id: mouseArea | ||
1242 | 320 | |||
1243 | 321 | property bool locked: root.locked || ((root.leftSideAction === null) && (root.rightSideActions.count === 0)) | ||
1244 | 322 | property bool manual: false | ||
1245 | 323 | |||
1246 | 324 | anchors.fill: parent | ||
1247 | 325 | drag { | ||
1248 | 326 | target: locked ? null : main | ||
1249 | 327 | axis: Drag.XAxis | ||
1250 | 328 | minimumX: rightActionsView.visible ? -(rightActionsView.width + root.actionThreshold) : 0 | ||
1251 | 329 | maximumX: leftActionView.visible ? leftActionView.width : 0 | ||
1252 | 330 | } | ||
1253 | 331 | |||
1254 | 332 | onReleased: { | ||
1255 | 333 | if (root.triggerActionOnMouseRelease && root.activeAction) { | ||
1256 | 334 | triggerAction.start() | ||
1257 | 335 | } else { | ||
1258 | 336 | root.returnToBounds() | ||
1259 | 337 | root.activeAction = null | ||
1260 | 338 | } | ||
1261 | 339 | } | ||
1262 | 340 | onClicked: { | ||
1263 | 341 | if (main.x === 0) { | ||
1264 | 342 | root.itemClicked(mouse) | ||
1265 | 343 | } else if (main.x > 0) { | ||
1266 | 344 | var action = getActionAt(Qt.point(mouse.x, mouse.y)) | ||
1267 | 345 | if (action && action !== -1) { | ||
1268 | 346 | action.triggered(root) | ||
1269 | 347 | } | ||
1270 | 348 | } else { | ||
1271 | 349 | var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y)) | ||
1272 | 350 | if (actionIndex !== -1) { | ||
1273 | 351 | root.activeItem = rightActionsRepeater.itemAt(actionIndex) | ||
1274 | 352 | root.activeAction = root.rightSideActions[actionIndex] | ||
1275 | 353 | triggerAction.start() | ||
1276 | 354 | return | ||
1277 | 355 | } | ||
1278 | 356 | } | ||
1279 | 357 | root.resetSwipe() | ||
1280 | 358 | } | ||
1281 | 359 | |||
1282 | 360 | onPositionChanged: { | ||
1283 | 361 | if (mouseArea.pressed) { | ||
1284 | 362 | updateActiveAction() | ||
1285 | 363 | } | ||
1286 | 364 | } | ||
1287 | 365 | onPressAndHold: { | ||
1288 | 366 | if (main.x === 0) { | ||
1289 | 367 | root.itemPressAndHold(mouse) | ||
1290 | 368 | } | ||
1291 | 369 | } | ||
1292 | 370 | z: -1 | ||
1293 | 371 | } | ||
1294 | 372 | } | ||
1295 | 373 | 0 | ||
1296 | === removed file 'src/app/webbrowser/upstreamcomponents/ListItemWithActionsCheckBox.qml' | |||
1297 | --- src/app/webbrowser/upstreamcomponents/ListItemWithActionsCheckBox.qml 2014-10-15 09:38:37 +0000 | |||
1298 | +++ src/app/webbrowser/upstreamcomponents/ListItemWithActionsCheckBox.qml 1970-01-01 00:00:00 +0000 | |||
1299 | @@ -1,25 +0,0 @@ | |||
1300 | 1 | /* | ||
1301 | 2 | * Copyright (C) 2012-2014 Canonical, Ltd. | ||
1302 | 3 | * | ||
1303 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1304 | 5 | * it under the terms of the GNU General Public License as published by | ||
1305 | 6 | * the Free Software Foundation; version 3. | ||
1306 | 7 | * | ||
1307 | 8 | * This program is distributed in the hope that it will be useful, | ||
1308 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1309 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1310 | 11 | * GNU General Public License for more details. | ||
1311 | 12 | * | ||
1312 | 13 | * You should have received a copy of the GNU General Public License | ||
1313 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1314 | 15 | */ | ||
1315 | 16 | |||
1316 | 17 | import QtQuick 2.0 | ||
1317 | 18 | import Ubuntu.Components 1.1 | ||
1318 | 19 | |||
1319 | 20 | CheckBox { | ||
1320 | 21 | checked: root.selected | ||
1321 | 22 | width: implicitWidth | ||
1322 | 23 | // disable item mouse area to avoid conflicts with parent mouse area | ||
1323 | 24 | __mouseArea.enabled: false | ||
1324 | 25 | } | ||
1325 | 26 | 0 | ||
1326 | === removed file 'src/app/webbrowser/upstreamcomponents/MultipleSelectionListView.qml' | |||
1327 | --- src/app/webbrowser/upstreamcomponents/MultipleSelectionListView.qml 2014-10-15 09:38:37 +0000 | |||
1328 | +++ src/app/webbrowser/upstreamcomponents/MultipleSelectionListView.qml 1970-01-01 00:00:00 +0000 | |||
1329 | @@ -1,199 +0,0 @@ | |||
1330 | 1 | /* | ||
1331 | 2 | * Copyright (C) 2013 Canonical, Ltd. | ||
1332 | 3 | * | ||
1333 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1334 | 5 | * it under the terms of the GNU General Public License as published by | ||
1335 | 6 | * the Free Software Foundation; version 3. | ||
1336 | 7 | * | ||
1337 | 8 | * This program is distributed in the hope that it will be useful, | ||
1338 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1339 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1340 | 11 | * GNU General Public License for more details. | ||
1341 | 12 | * | ||
1342 | 13 | * You should have received a copy of the GNU General Public License | ||
1343 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1344 | 15 | */ | ||
1345 | 16 | |||
1346 | 17 | import QtQuick 2.0 | ||
1347 | 18 | import Ubuntu.Components 1.1 | ||
1348 | 19 | import Ubuntu.Components.Popups 1.0 as Popups | ||
1349 | 20 | |||
1350 | 21 | /*! | ||
1351 | 22 | \qmltype ContactSimpleListView | ||
1352 | 23 | \inqmlmodule Ubuntu.Contacts 0.1 | ||
1353 | 24 | \ingroup ubuntu | ||
1354 | 25 | \brief The MultipleSelectionListView provides a ListView with support to multiple selection | ||
1355 | 26 | |||
1356 | 27 | The MultipleSelectionListViewprovides a ListView with support to multiple selection which can be used by any | ||
1357 | 28 | application. | ||
1358 | 29 | |||
1359 | 30 | Example: | ||
1360 | 31 | \qml | ||
1361 | 32 | import Ubuntu.Contacts 0.1 | ||
1362 | 33 | |||
1363 | 34 | MultipleSelectionListView { | ||
1364 | 35 | id: view | ||
1365 | 36 | anchors.fill: paret | ||
1366 | 37 | model: 100 | ||
1367 | 38 | delegate: Rectangle { | ||
1368 | 39 | width: parent.width | ||
1369 | 40 | height: 100 | ||
1370 | 41 | color: view.selectedItems.indexOf(index) == -1 ? "white" : "blue" | ||
1371 | 42 | |||
1372 | 43 | MouseArea { | ||
1373 | 44 | anchors.fill: parent | ||
1374 | 45 | onClicked: { | ||
1375 | 46 | if (view.isInSelectionModel) { | ||
1376 | 47 | view.selectItem(index) | ||
1377 | 48 | } | ||
1378 | 49 | } | ||
1379 | 50 | onPressAndHold: view.startSelection() | ||
1380 | 51 | } | ||
1381 | 52 | } | ||
1382 | 53 | onSelectionDone: console.debug("Selected items:" + view.selectedItems) | ||
1383 | 54 | } | ||
1384 | 55 | \endqml | ||
1385 | 56 | */ | ||
1386 | 57 | |||
1387 | 58 | ListView { | ||
1388 | 59 | id: listView | ||
1389 | 60 | |||
1390 | 61 | /*! | ||
1391 | 62 | \qmlproperty model selectedItems | ||
1392 | 63 | |||
1393 | 64 | This property holds the list of selected items | ||
1394 | 65 | */ | ||
1395 | 66 | readonly property alias selectedItems: visualModel.selectedItems | ||
1396 | 67 | /*! | ||
1397 | 68 | \qmlproperty bool multipleSelection | ||
1398 | 69 | |||
1399 | 70 | This property holds if the selection will accept multiple items or single items | ||
1400 | 71 | */ | ||
1401 | 72 | property bool multipleSelection: true | ||
1402 | 73 | |||
1403 | 74 | /*! | ||
1404 | 75 | \qmlproperty model listModel | ||
1405 | 76 | |||
1406 | 77 | This property holds the model providing data for the list. | ||
1407 | 78 | */ | ||
1408 | 79 | property alias listModel: visualModel.model | ||
1409 | 80 | /*! | ||
1410 | 81 | \qmlproperty Component listDelegate | ||
1411 | 82 | |||
1412 | 83 | The delegate provides a template defining each item instantiated by the view. | ||
1413 | 84 | */ | ||
1414 | 85 | property alias listDelegate: visualModel.delegate | ||
1415 | 86 | |||
1416 | 87 | /*! | ||
1417 | 88 | \qmlproperty bool isInSelectionMode | ||
1418 | 89 | |||
1419 | 90 | This property holds a list with the index of selected items | ||
1420 | 91 | */ | ||
1421 | 92 | readonly property bool isInSelectionMode: state === "selection" | ||
1422 | 93 | /*! | ||
1423 | 94 | This handler is called when the selection mode is finished without be canceled | ||
1424 | 95 | */ | ||
1425 | 96 | signal selectionDone(var items) | ||
1426 | 97 | /*! | ||
1427 | 98 | This handler is called when the selection mode is canceled | ||
1428 | 99 | */ | ||
1429 | 100 | signal selectionCanceled() | ||
1430 | 101 | |||
1431 | 102 | /*! | ||
1432 | 103 | Start the selection mode on the list view. | ||
1433 | 104 | */ | ||
1434 | 105 | function startSelection() | ||
1435 | 106 | { | ||
1436 | 107 | state = "selection" | ||
1437 | 108 | } | ||
1438 | 109 | /*! | ||
1439 | 110 | Check if the item is selected | ||
1440 | 111 | Returns true if the item was marked as selected or false if the item is unselected | ||
1441 | 112 | */ | ||
1442 | 113 | function isSelected(item) | ||
1443 | 114 | { | ||
1444 | 115 | if (item && item.VisualDataModel) { | ||
1445 | 116 | return (item.VisualDataModel.inSelected === true) | ||
1446 | 117 | } else { | ||
1447 | 118 | return false | ||
1448 | 119 | } | ||
1449 | 120 | } | ||
1450 | 121 | /*! | ||
1451 | 122 | Mark the item as selected | ||
1452 | 123 | Returns true if the item was marked as selected or false if the item is already selected | ||
1453 | 124 | */ | ||
1454 | 125 | function selectItem(item) | ||
1455 | 126 | { | ||
1456 | 127 | if (item.VisualDataModel.inSelected) { | ||
1457 | 128 | return false | ||
1458 | 129 | } else { | ||
1459 | 130 | if (!multipleSelection) { | ||
1460 | 131 | clearSelection() | ||
1461 | 132 | } | ||
1462 | 133 | item.VisualDataModel.inSelected = true | ||
1463 | 134 | return true | ||
1464 | 135 | } | ||
1465 | 136 | } | ||
1466 | 137 | /*! | ||
1467 | 138 | Remove the index from the selected list | ||
1468 | 139 | */ | ||
1469 | 140 | function deselectItem(item) | ||
1470 | 141 | { | ||
1471 | 142 | var result = false | ||
1472 | 143 | if (item.VisualDataModel.inSelected) { | ||
1473 | 144 | item.VisualDataModel.inSelected = false | ||
1474 | 145 | result = true | ||
1475 | 146 | } | ||
1476 | 147 | return result | ||
1477 | 148 | } | ||
1478 | 149 | /*! | ||
1479 | 150 | Finish the selection mode with sucess | ||
1480 | 151 | */ | ||
1481 | 152 | function endSelection() | ||
1482 | 153 | { | ||
1483 | 154 | selectionDone(listView.selectedItems) | ||
1484 | 155 | clearSelection() | ||
1485 | 156 | state = "" | ||
1486 | 157 | } | ||
1487 | 158 | /*! | ||
1488 | 159 | Cancel the selection | ||
1489 | 160 | */ | ||
1490 | 161 | function cancelSelection() | ||
1491 | 162 | { | ||
1492 | 163 | selectionCanceled() | ||
1493 | 164 | clearSelection() | ||
1494 | 165 | state = "" | ||
1495 | 166 | } | ||
1496 | 167 | /*! | ||
1497 | 168 | Remove any selected item from the selection list | ||
1498 | 169 | */ | ||
1499 | 170 | function clearSelection() | ||
1500 | 171 | { | ||
1501 | 172 | if (selectedItems.count > 0) { | ||
1502 | 173 | selectedItems.remove(0, selectedItems.count) | ||
1503 | 174 | } | ||
1504 | 175 | } | ||
1505 | 176 | /*! | ||
1506 | 177 | Select all items in the list | ||
1507 | 178 | */ | ||
1508 | 179 | function selectAll() | ||
1509 | 180 | { | ||
1510 | 181 | if (multipleSelection) { | ||
1511 | 182 | visualModel.items.addGroups(0, visualModel.items.count, ["selected"] ) | ||
1512 | 183 | } | ||
1513 | 184 | } | ||
1514 | 185 | |||
1515 | 186 | model: visualModel | ||
1516 | 187 | |||
1517 | 188 | MultipleSelectionVisualModel { | ||
1518 | 189 | id: visualModel | ||
1519 | 190 | } | ||
1520 | 191 | |||
1521 | 192 | Component.onCompleted: { | ||
1522 | 193 | // FIXME: workaround for qtubuntu not returning values depending on the grid unit definition | ||
1523 | 194 | // for Flickable.maximumFlickVelocity and Flickable.flickDeceleration | ||
1524 | 195 | var scaleFactor = units.gridUnit / 8; | ||
1525 | 196 | maximumFlickVelocity = maximumFlickVelocity * scaleFactor; | ||
1526 | 197 | flickDeceleration = flickDeceleration * scaleFactor; | ||
1527 | 198 | } | ||
1528 | 199 | } | ||
1529 | 200 | 0 | ||
1530 | === removed file 'src/app/webbrowser/upstreamcomponents/MultipleSelectionVisualModel.qml' | |||
1531 | --- src/app/webbrowser/upstreamcomponents/MultipleSelectionVisualModel.qml 2014-10-15 09:38:37 +0000 | |||
1532 | +++ src/app/webbrowser/upstreamcomponents/MultipleSelectionVisualModel.qml 1970-01-01 00:00:00 +0000 | |||
1533 | @@ -1,31 +0,0 @@ | |||
1534 | 1 | /* | ||
1535 | 2 | * Copyright (C) 2012-2013 Canonical, Ltd. | ||
1536 | 3 | * | ||
1537 | 4 | * This program is free software; you can redistribute it and/or modify | ||
1538 | 5 | * it under the terms of the GNU General Public License as published by | ||
1539 | 6 | * the Free Software Foundation; version 3. | ||
1540 | 7 | * | ||
1541 | 8 | * This program is distributed in the hope that it will be useful, | ||
1542 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1543 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1544 | 11 | * GNU General Public License for more details. | ||
1545 | 12 | * | ||
1546 | 13 | * You should have received a copy of the GNU General Public License | ||
1547 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1548 | 15 | */ | ||
1549 | 16 | |||
1550 | 17 | import QtQuick 2.0 | ||
1551 | 18 | |||
1552 | 19 | VisualDataModel { | ||
1553 | 20 | id: contactVisualModel | ||
1554 | 21 | |||
1555 | 22 | property alias selectedItems: selectedGroup | ||
1556 | 23 | |||
1557 | 24 | groups: [ | ||
1558 | 25 | VisualDataGroup { | ||
1559 | 26 | id: selectedGroup | ||
1560 | 27 | |||
1561 | 28 | name: "selected" | ||
1562 | 29 | } | ||
1563 | 30 | ] | ||
1564 | 31 | } | ||
1565 | 32 | 0 | ||
1566 | === removed file 'src/app/webbrowser/upstreamcomponents/README' | |||
1567 | --- src/app/webbrowser/upstreamcomponents/README 2014-10-09 17:41:02 +0000 | |||
1568 | +++ src/app/webbrowser/upstreamcomponents/README 1970-01-01 00:00:00 +0000 | |||
1569 | @@ -1,20 +0,0 @@ | |||
1570 | 1 | # Upstream components | ||
1571 | 2 | |||
1572 | 3 | This folder houses components that were taken from upstream applications like | ||
1573 | 4 | the Contacts App, Address Book etc. It is important to keep these files synced | ||
1574 | 5 | with upstream regularly. These components will be used app until they are made | ||
1575 | 6 | available in the Ubuntu SDK. | ||
1576 | 7 | |||
1577 | 8 | # Links | ||
1578 | 9 | |||
1579 | 10 | ListItemWithActions.qml | ||
1580 | 11 | https://bazaar.launchpad.net/~phablet-team/address-book-app/trunk/view/head:/src/imports/Ubuntu/Contacts/ListItemWithActions.qml | ||
1581 | 12 | |||
1582 | 13 | ListItemWithActionsCheckBox.qml | ||
1583 | 14 | https://bazaar.launchpad.net/~phablet-team/address-book-app/trunk/view/head:/src/imports/Ubuntu/Contacts/ListItemWithActionsCheckBox.qml | ||
1584 | 15 | |||
1585 | 16 | MultipleSelectionListView.qml | ||
1586 | 17 | https://bazaar.launchpad.net/~phablet-team/address-book-app/trunk/view/head:/src/imports/Ubuntu/Contacts/MultipleSelectionListView.qml | ||
1587 | 18 | |||
1588 | 19 | MultipleSelectionVisualModel.qml | ||
1589 | 20 | https://bazaar.launchpad.net/~phablet-team/address-book-app/trunk/view/head:/src/imports/Ubuntu/Contacts/MultipleSelectionVisualModel.qml | ||
1590 | 21 | 0 | ||
1591 | === modified file 'tests/autopilot/webbrowser_app/emulators/browser.py' | |||
1592 | --- tests/autopilot/webbrowser_app/emulators/browser.py 2015-05-20 11:31:33 +0000 | |||
1593 | +++ tests/autopilot/webbrowser_app/emulators/browser.py 2015-06-02 14:24:15 +0000 | |||
1594 | @@ -361,14 +361,32 @@ | |||
1595 | 361 | 361 | ||
1596 | 362 | class NewTabView(uitk.UbuntuUIToolkitCustomProxyObjectBase): | 362 | class NewTabView(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
1597 | 363 | 363 | ||
1603 | 364 | def get_top_sites(self): | 364 | def get_bookmarks_more_button(self): |
1604 | 365 | """Return a list of the top sites URLs.""" | 365 | return self.select_single("Button", objectName="bookmarks.moreButton") |
1605 | 366 | top_sites_list = self.wait_select_single( | 366 | |
1606 | 367 | UrlsList, objectName="topSitesList", visible=True) | 367 | def get_homepage_bookmark(self): |
1607 | 368 | return top_sites_list.get_url_list() | 368 | return self.select_single(UrlDelegate, objectName="homepageBookmark") |
1608 | 369 | |||
1609 | 370 | def get_bookmarks_list(self): | ||
1610 | 371 | return self.select_single(UrlsList, objectName="bookmarksList") | ||
1611 | 372 | |||
1612 | 373 | def get_top_sites_list(self): | ||
1613 | 374 | return self.select_single(UrlsList, objectName="topSitesList") | ||
1614 | 375 | |||
1615 | 376 | def get_notopsites_label(self): | ||
1616 | 377 | return self.select_single("Label", objectName="notopsites") | ||
1617 | 369 | 378 | ||
1618 | 370 | 379 | ||
1619 | 371 | class UrlsList(uitk.UbuntuUIToolkitCustomProxyObjectBase): | 380 | class UrlsList(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
1620 | 372 | 381 | ||
1623 | 373 | def get_url_list(self): | 382 | def get_delegates(self): |
1624 | 374 | return [delegate.url for delegate in self.select_many("UrlDelegate")] | 383 | return sorted(self.select_many(UrlDelegate), |
1625 | 384 | key=lambda delegate: delegate.globalRect.y) | ||
1626 | 385 | |||
1627 | 386 | def get_urls(self): | ||
1628 | 387 | return [delegate.url for delegate in self.get_delegates()] | ||
1629 | 388 | |||
1630 | 389 | |||
1631 | 390 | class UrlDelegate(uitk.UCListItem): | ||
1632 | 391 | |||
1633 | 392 | pass | ||
1634 | 375 | 393 | ||
1635 | === modified file 'tests/autopilot/webbrowser_app/tests/__init__.py' | |||
1636 | --- tests/autopilot/webbrowser_app/tests/__init__.py 2015-05-20 11:37:18 +0000 | |||
1637 | +++ tests/autopilot/webbrowser_app/tests/__init__.py 2015-06-02 14:24:15 +0000 | |||
1638 | @@ -134,7 +134,7 @@ | |||
1639 | 134 | self.pointing_device.click_object(tabs_action) | 134 | self.pointing_device.click_object(tabs_action) |
1640 | 135 | else: | 135 | else: |
1641 | 136 | self.drag_bottom_edge_upwards(0.75) | 136 | self.drag_bottom_edge_upwards(0.75) |
1643 | 137 | self.main_window.get_tabs_view() | 137 | return self.main_window.get_tabs_view() |
1644 | 138 | 138 | ||
1645 | 139 | def open_new_tab(self): | 139 | def open_new_tab(self): |
1646 | 140 | if (self.main_window.incognito): | 140 | if (self.main_window.incognito): |
1647 | 141 | 141 | ||
1648 | === added file 'tests/autopilot/webbrowser_app/tests/test_new_tab_view.py' | |||
1649 | --- tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 1970-01-01 00:00:00 +0000 | |||
1650 | +++ tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2015-06-02 14:24:15 +0000 | |||
1651 | @@ -0,0 +1,274 @@ | |||
1652 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
1653 | 2 | # | ||
1654 | 3 | # Copyright 2015 Canonical | ||
1655 | 4 | # | ||
1656 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
1657 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
1658 | 7 | # by the Free Software Foundation. | ||
1659 | 8 | # | ||
1660 | 9 | # This program is distributed in the hope that it will be useful, | ||
1661 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1662 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1663 | 12 | # GNU General Public License for more details. | ||
1664 | 13 | # | ||
1665 | 14 | # You should have received a copy of the GNU General Public License | ||
1666 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1667 | 16 | |||
1668 | 17 | import os.path | ||
1669 | 18 | import sqlite3 | ||
1670 | 19 | import time | ||
1671 | 20 | |||
1672 | 21 | from autopilot.matchers import Eventually | ||
1673 | 22 | from testtools.matchers import Equals, NotEquals | ||
1674 | 23 | |||
1675 | 24 | from webbrowser_app.tests import StartOpenRemotePageTestCaseBase | ||
1676 | 25 | |||
1677 | 26 | |||
1678 | 27 | class TestNewTabViewLifetime(StartOpenRemotePageTestCaseBase): | ||
1679 | 28 | |||
1680 | 29 | def test_new_tab_view_destroyed_when_browsing(self): | ||
1681 | 30 | self.open_tabs_view() | ||
1682 | 31 | new_tab_view = self.open_new_tab() | ||
1683 | 32 | self.main_window.go_to_url(self.base_url + "/test2") | ||
1684 | 33 | new_tab_view.wait_until_destroyed() | ||
1685 | 34 | |||
1686 | 35 | def test_new_tab_view_destroyed_when_closing_tab(self): | ||
1687 | 36 | self.open_tabs_view() | ||
1688 | 37 | new_tab_view = self.open_new_tab() | ||
1689 | 38 | tabs_view = self.open_tabs_view() | ||
1690 | 39 | tabs_view.get_previews()[0].close() | ||
1691 | 40 | self.main_window.get_recent_view_toolbar().click_button("doneButton") | ||
1692 | 41 | new_tab_view.wait_until_destroyed() | ||
1693 | 42 | |||
1694 | 43 | def test_new_tab_view_is_shared_between_tabs(self): | ||
1695 | 44 | # Open one new tab | ||
1696 | 45 | self.open_tabs_view() | ||
1697 | 46 | new_tab_view = self.open_new_tab() | ||
1698 | 47 | # Open a second new tab | ||
1699 | 48 | self.open_tabs_view() | ||
1700 | 49 | new_tab_view_2 = self.open_new_tab() | ||
1701 | 50 | # Verify that they share the same NewTabView instance | ||
1702 | 51 | self.assertThat(new_tab_view_2.id, Equals(new_tab_view.id)) | ||
1703 | 52 | # Close the second new tab, and verify that the NewTabView instance | ||
1704 | 53 | # is still there | ||
1705 | 54 | tabs_view = self.open_tabs_view() | ||
1706 | 55 | tabs_view.get_previews()[0].close() | ||
1707 | 56 | self.main_window.get_recent_view_toolbar().click_button("doneButton") | ||
1708 | 57 | tabs_view.visible.wait_for(False) | ||
1709 | 58 | self.assertThat(new_tab_view.visible, Equals(True)) | ||
1710 | 59 | # Close the first new tab, and verify that the NewTabView instance | ||
1711 | 60 | # is destroyed | ||
1712 | 61 | tabs_view = self.open_tabs_view() | ||
1713 | 62 | tabs_view.get_previews()[0].close() | ||
1714 | 63 | self.main_window.get_recent_view_toolbar().click_button("doneButton") | ||
1715 | 64 | new_tab_view.wait_until_destroyed() | ||
1716 | 65 | |||
1717 | 66 | def test_new_tab_view_not_destroyed_when_closing_last_open_tab(self): | ||
1718 | 67 | tabs_view = self.open_tabs_view() | ||
1719 | 68 | tabs_view.get_previews()[0].close() | ||
1720 | 69 | tabs_view.visible.wait_for(False) | ||
1721 | 70 | new_tab_view = self.main_window.get_new_tab_view() | ||
1722 | 71 | # Verify that the new tab view is not destroyed and then re-created | ||
1723 | 72 | # when closing the last open tab if it was a blank one | ||
1724 | 73 | tabs_view = self.open_tabs_view() | ||
1725 | 74 | tabs_view.get_previews()[0].close() | ||
1726 | 75 | tabs_view.visible.wait_for(False) | ||
1727 | 76 | self.assertThat(new_tab_view.visible, Equals(True)) | ||
1728 | 77 | |||
1729 | 78 | |||
1730 | 79 | class TestNewPrivateTabViewLifetime(StartOpenRemotePageTestCaseBase): | ||
1731 | 80 | |||
1732 | 81 | def test_new_private_tab_view_destroyed_when_browsing(self): | ||
1733 | 82 | self.main_window.enter_private_mode() | ||
1734 | 83 | new_private_tab_view = self.main_window.get_new_private_tab_view() | ||
1735 | 84 | self.main_window.go_to_url(self.base_url + "/test2") | ||
1736 | 85 | new_private_tab_view.wait_until_destroyed() | ||
1737 | 86 | |||
1738 | 87 | def test_new_private_tab_view_destroyed_when_leaving_private_mode(self): | ||
1739 | 88 | self.main_window.enter_private_mode() | ||
1740 | 89 | new_private_tab_view = self.main_window.get_new_private_tab_view() | ||
1741 | 90 | self.main_window.leave_private_mode() | ||
1742 | 91 | new_private_tab_view.wait_until_destroyed() | ||
1743 | 92 | |||
1744 | 93 | def test_new_private_tab_view_is_shared_between_tabs(self): | ||
1745 | 94 | self.main_window.enter_private_mode() | ||
1746 | 95 | new_private_tab_view = self.main_window.get_new_private_tab_view() | ||
1747 | 96 | self.main_window.go_to_url(self.base_url + "/test2") | ||
1748 | 97 | new_private_tab_view.wait_until_destroyed() | ||
1749 | 98 | # Open one new private tab | ||
1750 | 99 | self.open_tabs_view() | ||
1751 | 100 | new_private_tab_view = self.open_new_tab() | ||
1752 | 101 | # Open a second new private tab | ||
1753 | 102 | self.open_tabs_view() | ||
1754 | 103 | new_private_tab_view_2 = self.open_new_tab() | ||
1755 | 104 | # Verify that they share the same NewPrivateTabView instance | ||
1756 | 105 | self.assertThat(new_private_tab_view_2.id, | ||
1757 | 106 | Equals(new_private_tab_view.id)) | ||
1758 | 107 | # Close the second new private tab, and verify that the | ||
1759 | 108 | # NewPrivateTabView instance is still there | ||
1760 | 109 | tabs_view = self.open_tabs_view() | ||
1761 | 110 | tabs_view.get_previews()[0].close() | ||
1762 | 111 | self.main_window.get_recent_view_toolbar().click_button("doneButton") | ||
1763 | 112 | tabs_view.visible.wait_for(False) | ||
1764 | 113 | self.assertThat(new_private_tab_view.visible, Equals(True)) | ||
1765 | 114 | # Close the first new private tab, and verify that the | ||
1766 | 115 | # NewPrivateTabView instance is destroyed | ||
1767 | 116 | tabs_view = self.open_tabs_view() | ||
1768 | 117 | tabs_view.get_previews()[0].close() | ||
1769 | 118 | self.main_window.get_recent_view_toolbar().click_button("doneButton") | ||
1770 | 119 | new_private_tab_view.wait_until_destroyed() | ||
1771 | 120 | |||
1772 | 121 | |||
1773 | 122 | class TestNewTabViewContents(StartOpenRemotePageTestCaseBase): | ||
1774 | 123 | |||
1775 | 124 | def setUp(self): | ||
1776 | 125 | self.create_temporary_profile() | ||
1777 | 126 | self.populate_config() | ||
1778 | 127 | self.populate_bookmarks() | ||
1779 | 128 | super(TestNewTabViewContents, self).setUp() | ||
1780 | 129 | |||
1781 | 130 | def populate_config(self): | ||
1782 | 131 | self.homepage = "http://test/test2" | ||
1783 | 132 | config_file = os.path.join(self.config_location, "webbrowser-app.conf") | ||
1784 | 133 | with open(config_file, "w") as f: | ||
1785 | 134 | f.write("[General]\n") | ||
1786 | 135 | f.write("homepage={}".format(self.homepage)) | ||
1787 | 136 | |||
1788 | 137 | def populate_bookmarks(self): | ||
1789 | 138 | db_path = os.path.join(self.data_location, "bookmarks.sqlite") | ||
1790 | 139 | connection = sqlite3.connect(db_path) | ||
1791 | 140 | connection.execute("""CREATE TABLE IF NOT EXISTS bookmarks | ||
1792 | 141 | (url VARCHAR, title VARCHAR, icon VARCHAR, | ||
1793 | 142 | created INTEGER);""") | ||
1794 | 143 | rows = [ | ||
1795 | 144 | ("http://test/periodic-table/element/24/chromium", | ||
1796 | 145 | "Chromium - Element Information"), | ||
1797 | 146 | ("http://test/periodic-table/element/77/iridium", | ||
1798 | 147 | "Iridium - Element Information"), | ||
1799 | 148 | ("http://test/periodic-table/element/31/gallium", | ||
1800 | 149 | "Gallium - Element Information"), | ||
1801 | 150 | ("http://test/periodic-table/element/116/livermorium", | ||
1802 | 151 | "Livermorium - Element Information"), | ||
1803 | 152 | ("http://test/periodic-table/element/62/samarium", | ||
1804 | 153 | "Samarium - Element Information"), | ||
1805 | 154 | ("http://test/periodic-table/element/63/europium", | ||
1806 | 155 | "Europium - Element Information"), | ||
1807 | 156 | ] | ||
1808 | 157 | for i, row in enumerate(rows): | ||
1809 | 158 | timestamp = int(time.time()) - i * 10 | ||
1810 | 159 | query = "INSERT INTO bookmarks \ | ||
1811 | 160 | VALUES ('{}', '{}', '', {});" | ||
1812 | 161 | query = query.format(row[0], row[1], timestamp) | ||
1813 | 162 | connection.execute(query) | ||
1814 | 163 | connection.commit() | ||
1815 | 164 | connection.close() | ||
1816 | 165 | |||
1817 | 166 | def test_default_home_bookmark(self): | ||
1818 | 167 | self.open_tabs_view() | ||
1819 | 168 | new_tab_view = self.open_new_tab() | ||
1820 | 169 | homepage_bookmark = new_tab_view.get_homepage_bookmark() | ||
1821 | 170 | self.assertThat(homepage_bookmark.url, Equals(self.homepage)) | ||
1822 | 171 | self.pointing_device.click_object(homepage_bookmark) | ||
1823 | 172 | new_tab_view.wait_until_destroyed() | ||
1824 | 173 | self.main_window.wait_until_page_loaded(self.homepage) | ||
1825 | 174 | |||
1826 | 175 | def test_open_bookmark(self): | ||
1827 | 176 | self.open_tabs_view() | ||
1828 | 177 | new_tab_view = self.open_new_tab() | ||
1829 | 178 | bookmarks = new_tab_view.get_bookmarks_list() | ||
1830 | 179 | self.assertThat(lambda: len(bookmarks.get_delegates()), | ||
1831 | 180 | Eventually(Equals(4))) | ||
1832 | 181 | bookmark = bookmarks.get_delegates()[1] | ||
1833 | 182 | url = bookmark.url | ||
1834 | 183 | self.pointing_device.click_object(bookmark) | ||
1835 | 184 | new_tab_view.wait_until_destroyed() | ||
1836 | 185 | self.main_window.wait_until_page_loaded(url) | ||
1837 | 186 | |||
1838 | 187 | def test_bookmarks_section_expands_and_collapses(self): | ||
1839 | 188 | self.open_tabs_view() | ||
1840 | 189 | new_tab_view = self.open_new_tab() | ||
1841 | 190 | bookmarks = new_tab_view.get_bookmarks_list() | ||
1842 | 191 | top_sites = new_tab_view.get_top_sites_list() | ||
1843 | 192 | self.assertThat(top_sites.visible, Equals(True)) | ||
1844 | 193 | # When the bookmarks list is collapsed, it shows a maximum of 4 entries | ||
1845 | 194 | self.assertThat(lambda: len(bookmarks.get_delegates()), | ||
1846 | 195 | Eventually(Equals(4))) | ||
1847 | 196 | # When expanded, it shows all entries | ||
1848 | 197 | more_button = new_tab_view.get_bookmarks_more_button() | ||
1849 | 198 | self.assertThat(more_button.visible, Equals(True)) | ||
1850 | 199 | self.pointing_device.click_object(more_button) | ||
1851 | 200 | self.assertThat(lambda: len(bookmarks.get_delegates()), | ||
1852 | 201 | Eventually(Equals(6))) | ||
1853 | 202 | self.assertThat(top_sites.visible, Eventually(Equals(False))) | ||
1854 | 203 | # Collapse again | ||
1855 | 204 | self.assertThat(more_button.visible, Equals(True)) | ||
1856 | 205 | self.pointing_device.click_object(more_button) | ||
1857 | 206 | self.assertThat(lambda: len(bookmarks.get_delegates()), | ||
1858 | 207 | Eventually(Equals(4))) | ||
1859 | 208 | self.assertThat(top_sites.visible, Eventually(Equals(True))) | ||
1860 | 209 | |||
1861 | 210 | def _remove_first_bookmark(self): | ||
1862 | 211 | bookmarks = self.main_window.get_new_tab_view().get_bookmarks_list() | ||
1863 | 212 | delegate = bookmarks.get_delegates()[0] | ||
1864 | 213 | url = delegate.url | ||
1865 | 214 | delegate.trigger_leading_action("leadingAction.delete", | ||
1866 | 215 | delegate.wait_until_destroyed) | ||
1867 | 216 | self.assertThat(lambda: bookmarks.get_urls()[0], | ||
1868 | 217 | Eventually(NotEquals(url))) | ||
1869 | 218 | |||
1870 | 219 | def test_remove_bookmarks_when_collapsed(self): | ||
1871 | 220 | self.open_tabs_view() | ||
1872 | 221 | new_tab_view = self.open_new_tab() | ||
1873 | 222 | bookmarks = new_tab_view.get_bookmarks_list() | ||
1874 | 223 | self.assertThat(lambda: len(bookmarks.get_delegates()), | ||
1875 | 224 | Eventually(Equals(4))) | ||
1876 | 225 | more_button = new_tab_view.get_bookmarks_more_button() | ||
1877 | 226 | for i in range(3): | ||
1878 | 227 | self._remove_first_bookmark() | ||
1879 | 228 | self.assertThat(more_button.visible, Eventually(Equals(i < 1))) | ||
1880 | 229 | self.assertThat(len(bookmarks.get_delegates()), | ||
1881 | 230 | Equals(4 if (i < 2) else 3)) | ||
1882 | 231 | |||
1883 | 232 | def test_remove_bookmarks_when_expanded(self): | ||
1884 | 233 | self.open_tabs_view() | ||
1885 | 234 | new_tab_view = self.open_new_tab() | ||
1886 | 235 | bookmarks = new_tab_view.get_bookmarks_list() | ||
1887 | 236 | more_button = new_tab_view.get_bookmarks_more_button() | ||
1888 | 237 | self.assertThat(more_button.visible, Equals(True)) | ||
1889 | 238 | self.pointing_device.click_object(more_button) | ||
1890 | 239 | self.assertThat(lambda: len(bookmarks.get_delegates()), | ||
1891 | 240 | Eventually(Equals(6))) | ||
1892 | 241 | more_button = new_tab_view.get_bookmarks_more_button() | ||
1893 | 242 | top_sites = new_tab_view.get_top_sites_list() | ||
1894 | 243 | for i in range(3): | ||
1895 | 244 | self._remove_first_bookmark() | ||
1896 | 245 | self.assertThat(len(bookmarks.get_delegates()), Equals(5 - i)) | ||
1897 | 246 | self.assertThat(more_button.visible, Eventually(Equals(i < 1))) | ||
1898 | 247 | self.assertThat(top_sites.visible, Eventually(Equals(i > 0))) | ||
1899 | 248 | |||
1900 | 249 | def test_open_top_site(self): | ||
1901 | 250 | self.open_tabs_view() | ||
1902 | 251 | new_tab_view = self.open_new_tab() | ||
1903 | 252 | top_sites = new_tab_view.get_top_sites_list() | ||
1904 | 253 | self.assertThat(lambda: len(top_sites.get_delegates()), | ||
1905 | 254 | Eventually(Equals(1))) | ||
1906 | 255 | top_site = top_sites.get_delegates()[0] | ||
1907 | 256 | url = top_site.url | ||
1908 | 257 | self.pointing_device.click_object(top_site) | ||
1909 | 258 | new_tab_view.wait_until_destroyed() | ||
1910 | 259 | self.main_window.wait_until_page_loaded(url) | ||
1911 | 260 | |||
1912 | 261 | def test_remove_top_sites(self): | ||
1913 | 262 | self.open_tabs_view() | ||
1914 | 263 | new_tab_view = self.open_new_tab() | ||
1915 | 264 | top_sites = new_tab_view.get_top_sites_list() | ||
1916 | 265 | self.assertThat(lambda: len(top_sites.get_delegates()), | ||
1917 | 266 | Eventually(Equals(1))) | ||
1918 | 267 | notopsites_label = new_tab_view.get_notopsites_label() | ||
1919 | 268 | self.assertThat(notopsites_label.visible, Eventually(Equals(False))) | ||
1920 | 269 | delegate = top_sites.get_delegates()[0] | ||
1921 | 270 | delegate.trigger_leading_action("leadingAction.delete", | ||
1922 | 271 | delegate.wait_until_destroyed) | ||
1923 | 272 | self.assertThat(lambda: len(top_sites.get_delegates()), | ||
1924 | 273 | Eventually(Equals(0))) | ||
1925 | 274 | self.assertThat(notopsites_label.visible, Eventually(Equals(True))) | ||
1926 | 0 | 275 | ||
1927 | === modified file 'tests/autopilot/webbrowser_app/tests/test_private.py' | |||
1928 | --- tests/autopilot/webbrowser_app/tests/test_private.py 2015-05-22 19:13:31 +0000 | |||
1929 | +++ tests/autopilot/webbrowser_app/tests/test_private.py 2015-06-02 14:24:15 +0000 | |||
1930 | @@ -25,7 +25,7 @@ | |||
1931 | 25 | def get_url_list_from_top_sites(self): | 25 | def get_url_list_from_top_sites(self): |
1932 | 26 | self.open_tabs_view() | 26 | self.open_tabs_view() |
1933 | 27 | new_tab_view = self.open_new_tab() | 27 | new_tab_view = self.open_new_tab() |
1935 | 28 | return new_tab_view.get_top_sites() | 28 | return new_tab_view.get_top_sites_list().get_urls() |
1936 | 29 | 29 | ||
1937 | 30 | def test_going_in_and_out_private_mode(self): | 30 | def test_going_in_and_out_private_mode(self): |
1938 | 31 | address_bar = self.main_window.address_bar | 31 | address_bar = self.main_window.address_bar |
1939 | 32 | 32 | ||
1940 | === modified file 'tests/autopilot/webbrowser_app/tests/test_tabs.py' | |||
1941 | --- tests/autopilot/webbrowser_app/tests/test_tabs.py 2015-03-20 15:28:33 +0000 | |||
1942 | +++ tests/autopilot/webbrowser_app/tests/test_tabs.py 2015-06-02 14:24:15 +0000 | |||
1943 | @@ -71,8 +71,7 @@ | |||
1944 | 71 | self.main_window.go_to_url(url) | 71 | self.main_window.go_to_url(url) |
1945 | 72 | new_tab_view.wait_until_destroyed() | 72 | new_tab_view.wait_until_destroyed() |
1946 | 73 | self.assert_number_webviews_eventually(2) | 73 | self.assert_number_webviews_eventually(2) |
1949 | 74 | self.open_tabs_view() | 74 | tabs_view = self.open_tabs_view() |
1948 | 75 | tabs_view = self.main_window.get_tabs_view() | ||
1950 | 76 | previews = tabs_view.get_previews() | 75 | previews = tabs_view.get_previews() |
1951 | 77 | self.assertThat(len(previews), Equals(2)) | 76 | self.assertThat(len(previews), Equals(2)) |
1952 | 78 | previews[0].close() | 77 | previews[0].close() |
1953 | @@ -150,8 +149,7 @@ | |||
1954 | 150 | self.assert_number_webviews_eventually(2) | 149 | self.assert_number_webviews_eventually(2) |
1955 | 151 | 150 | ||
1956 | 152 | def test_selecting_tab_focuses_webview(self): | 151 | def test_selecting_tab_focuses_webview(self): |
1959 | 153 | self.open_tabs_view() | 152 | tabs_view = self.open_tabs_view() |
1958 | 154 | tabs_view = self.main_window.get_tabs_view() | ||
1960 | 155 | tabs_view.get_previews()[0].select() | 153 | tabs_view.get_previews()[0].select() |
1961 | 156 | tabs_view.visible.wait_for(False) | 154 | tabs_view.visible.wait_for(False) |
1962 | 157 | webview = self.main_window.get_current_webview() | 155 | webview = self.main_window.get_current_webview() |
1963 | @@ -170,8 +168,7 @@ | |||
1964 | 170 | self.assert_number_webviews_eventually(1) | 168 | self.assert_number_webviews_eventually(1) |
1965 | 171 | 169 | ||
1966 | 172 | def test_last_webview_requests_close(self): | 170 | def test_last_webview_requests_close(self): |
1969 | 173 | self.open_tabs_view() | 171 | tabs_view = self.open_tabs_view() |
1968 | 174 | tabs_view = self.main_window.get_tabs_view() | ||
1970 | 175 | tabs_view.get_previews()[0].close() | 172 | tabs_view.get_previews()[0].close() |
1971 | 176 | tabs_view.visible.wait_for(False) | 173 | tabs_view.visible.wait_for(False) |
1972 | 177 | url = self.base_url + "/closeself" | 174 | url = self.base_url + "/closeself" |
1973 | 178 | 175 | ||
1974 | === modified file 'tests/unittests/history-domainlist-chronological-model/tst_HistoryDomainListChronologicalModelTests.cpp' | |||
1975 | --- tests/unittests/history-domainlist-chronological-model/tst_HistoryDomainListChronologicalModelTests.cpp 2014-08-12 19:57:28 +0000 | |||
1976 | +++ tests/unittests/history-domainlist-chronological-model/tst_HistoryDomainListChronologicalModelTests.cpp 2015-06-02 14:24:15 +0000 | |||
1977 | @@ -99,6 +99,11 @@ | |||
1978 | 99 | QCOMPARE(args.at(1).toInt(), 0); | 99 | QCOMPARE(args.at(1).toInt(), 0); |
1979 | 100 | QCOMPARE(args.at(2).toInt(), 0); | 100 | QCOMPARE(args.at(2).toInt(), 0); |
1980 | 101 | } | 101 | } |
1981 | 102 | |||
1982 | 103 | void shouldReturnDomain() { | ||
1983 | 104 | history->add(QUrl("http://example.org/"), "Example Domain", QUrl()); | ||
1984 | 105 | QCOMPARE(model->get(0), QString("example.org")); | ||
1985 | 106 | } | ||
1986 | 102 | }; | 107 | }; |
1987 | 103 | 108 | ||
1988 | 104 | QTEST_MAIN(HistoryDomainListChronologicalModelTests) | 109 | QTEST_MAIN(HistoryDomainListChronologicalModelTests) |
1989 | 105 | 110 | ||
1990 | === modified file 'tests/unittests/top-sites-model/tst_TopSitesModelTests.cpp' | |||
1991 | --- tests/unittests/top-sites-model/tst_TopSitesModelTests.cpp 2015-04-09 15:03:27 +0000 | |||
1992 | +++ tests/unittests/top-sites-model/tst_TopSitesModelTests.cpp 2015-06-02 14:24:15 +0000 | |||
1993 | @@ -74,17 +74,22 @@ | |||
1994 | 74 | 74 | ||
1995 | 75 | void shouldMatchAllWhenNothingIsHidden() | 75 | void shouldMatchAllWhenNothingIsHidden() |
1996 | 76 | { | 76 | { |
1997 | 77 | QSignalSpy spy(topsites, SIGNAL(countChanged())); | ||
1998 | 77 | model->add(QUrl("http://example.org"), "Example Domain", QUrl()); | 78 | model->add(QUrl("http://example.org"), "Example Domain", QUrl()); |
1999 | 78 | model->add(QUrl("http://example.com"), "Example Domain", QUrl()); | 79 | model->add(QUrl("http://example.com"), "Example Domain", QUrl()); |
2000 | 80 | QCOMPARE(spy.count(), 2); | ||
2001 | 79 | QCOMPARE(topsites->rowCount(), 2); | 81 | QCOMPARE(topsites->rowCount(), 2); |
2002 | 80 | } | 82 | } |
2003 | 81 | 83 | ||
2004 | 82 | void shouldFilterOutHiddenUrls() | 84 | void shouldFilterOutHiddenUrls() |
2005 | 83 | { | 85 | { |
2006 | 86 | QSignalSpy spy(topsites, SIGNAL(countChanged())); | ||
2007 | 84 | model->add(QUrl("http://example.org"), "Example Domain", QUrl()); | 87 | model->add(QUrl("http://example.org"), "Example Domain", QUrl()); |
2008 | 85 | model->add(QUrl("http://example.com"), "Example Domain", QUrl()); | 88 | model->add(QUrl("http://example.com"), "Example Domain", QUrl()); |
2009 | 89 | QCOMPARE(spy.count(), 2); | ||
2010 | 86 | QCOMPARE(topsites->rowCount(), 2); | 90 | QCOMPARE(topsites->rowCount(), 2); |
2011 | 87 | model->hide(QUrl("http://example.org")); | 91 | model->hide(QUrl("http://example.org")); |
2012 | 92 | QCOMPARE(spy.count(), 3); | ||
2013 | 88 | QCOMPARE(topsites->rowCount(), 1); | 93 | QCOMPARE(topsites->rowCount(), 1); |
2014 | 89 | QCOMPARE(topsites->data(topsites->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://example.com")); | 94 | QCOMPARE(topsites->data(topsites->index(0, 0), HistoryModel::Url).toUrl(), QUrl("http://example.com")); |
2015 | 90 | } | 95 | } |
Since LP Web UI doesn't work for MR I'm doing this review by mail,
and I'll take more time to check the diff later.
It seems to work well, autopilot tests work good, there aren't flake8 issues
and the code seems ok.
Great work Olivier :-)