Merge lp:~artmello/webbrowser-app/webbrowser-app-bookmarks_top_sites into lp:webbrowser-app
- webbrowser-app-bookmarks_top_sites
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Olivier Tilloy |
Approved revision: | 592 |
Merged at revision: | 602 |
Proposed branch: | lp:~artmello/webbrowser-app/webbrowser-app-bookmarks_top_sites |
Merge into: | lp:webbrowser-app |
Diff against target: |
1331 lines (+1080/-12) 19 files modified
po/webbrowser-app.pot (+19/-6) src/app/webbrowser/BookmarksList.qml (+75/-0) src/app/webbrowser/Browser.qml (+29/-4) src/app/webbrowser/CMakeLists.txt (+2/-0) src/app/webbrowser/NewTabView.qml (+176/-0) src/app/webbrowser/UrlDelegate.qml (+73/-0) src/app/webbrowser/history-byvisits-model.cpp (+50/-0) src/app/webbrowser/history-byvisits-model.h (+43/-0) src/app/webbrowser/limit-proxy-model.cpp (+268/-0) src/app/webbrowser/limit-proxy-model.h (+70/-0) src/app/webbrowser/webbrowser-app.cpp (+4/-0) tests/autopilot/webbrowser_app/emulators/browser.py (+6/-0) tests/autopilot/webbrowser_app/tests/__init__.py (+8/-2) tests/autopilot/webbrowser_app/tests/test_tabs.py (+10/-0) tests/unittests/CMakeLists.txt (+2/-0) tests/unittests/history-byvisits-model/CMakeLists.txt (+6/-0) tests/unittests/history-byvisits-model/tst_HistoryByVisitsModelTests.cpp (+89/-0) tests/unittests/limit-proxy-model/CMakeLists.txt (+6/-0) tests/unittests/limit-proxy-model/tst_LimitProxyModelTests.cpp (+144/-0) |
To merge this branch: | bzr merge lp:~artmello/webbrowser-app/webbrowser-app-bookmarks_top_sites |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Needs Fixing | |
Olivier Tilloy | Approve | ||
Review via email:
|
Commit message
Add the Top Sites Sheet, showing Bookmarks and most visited URLs.
It is displayed when an empty new tab is created.
Description of the change
Add the Top Sites Sheet, showing Bookmarks and most visited URLs.
It is displaced when an empty new Tab is created
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Can you back out the changes to the bookmarks model? Ugo has been working on a branch to add the timestamp separately (see https:/
So until we merge Ugo’s branch (which needs some rework that I’ll probably take on), bookmarks would not be ordered by creation date in the new tab page, but that shouldn’t be too bad for a start.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Functional issue: I have two bookmarks in my database, and when opening the new tab page, there is a "see more" link, which, when clicked, expands the bookmarks section to take up the whole screen, thus hiding the history entries. I think the "see more" link should be visible only when there are actually more bookmarks to display.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Functional regression: if I bookmark new pages (the verify that they appear in the bookmarks view), then exit the browser and launch it again, the new bookmarks haven’t been stored in the database.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
As pointed out in a previous review round: the "see more/see less" action should probably trigger a smooth transition.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
The copyright notice for tests/unittests
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
What purpose does the TopSitesSheet component serve? It seems all it does is wrap a TopSitesView and proxy some of its properties and signals, so it looks like it could be removed.
On a related note, the naming of TopSitesView is slightly confusing, as it contains both top sites and bookmarks. A better name would probably be "NewTabPage" or "NewTabView".
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
It looks like this could be simplified:
249 + start: {
250 + var date = new Date()
251 + date.setDate(
252 + date.setHours(0)
253 + date.setMinutes(0)
254 + date.setSeconds(0)
255 + date.setMillise
256 + return date
257 + }
258 + end: {
259 + var date = new Date()
260 + date.setDate(
261 + date.setHours(23)
262 + date.setMinutes(59)
263 + date.setSeconds(59)
264 + date.setMillise
265 + return date
266 + }
For start, you don’t need to call setHours, setMinutes, setSeconds and setMilliseconds.
For end, you can just return a new Date(), i.e.: "end: new Date()"
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
As already discussed, in the current implementation TopSitesSheet is always instantiated. Instead, it should be loaded on demand (encapsulating it in a Component, or using a Loader), only when the user requests a new tab. It should probably be destroyed as soon as the URL of the current webview becomes non empty, and instantiated again when switching to a webview with an empty URL.
- 564. By Arthur Mello
-
Merge with trunk
- 565. By Arthur Mello
-
Back out the changes to the bookmarks model.
We are not using an ordered sort model anymore.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> Can you back out the changes to the bookmarks model? Ugo has been working on a
> branch to add the timestamp separately (see https:/
> /~phablet-
> timestamp/
> BookmarksChrono
> always sorted by creation date (AFAIK there is no use case for sorting them
> otherwise).
>
> So until we merge Ugo’s branch (which needs some rework that I’ll probably
> take on), bookmarks would not be ordered by creation date in the new tab page,
> but that shouldn’t be too bad for a start.
Fixed on revision 565
- 566. By Arthur Mello
-
Make "see more" link visible only when there are actually more bookmarks to display
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> Functional issue: I have two bookmarks in my database, and when opening the
> new tab page, there is a "see more" link, which, when clicked, expands the
> bookmarks section to take up the whole screen, thus hiding the history
> entries. I think the "see more" link should be visible only when there are
> actually more bookmarks to display.
Fixed on revision 566
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> Functional regression: if I bookmark new pages (the verify that they appear in
> the bookmarks view), then exit the browser and launch it again, the new
> bookmarks haven’t been stored in the database.
I was not able to reproduce the problem after rev 565, when we removed the changes on bookmarks model. Probably there was something wrong with the changes on bookmarks model or on bookmarks chronological model. If you still face the issue, please, let me know.
- 567. By Arthur Mello
-
Make the "see more/see less" action trigger a transition
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> As pointed out in a previous review round: the "see more/see less" action
> should probably trigger a smooth transition.
Fixed on revision 567
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> The copyright notice for tests/unittests
> model/tst_
The unittest was removed since the bookmarks chronological model is not used anymore
- 568. By Arthur Mello
-
Remove unnecessary TopSitesSheet
Rename TopSitesView to NewTabView
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> What purpose does the TopSitesSheet component serve? It seems all it does is
> wrap a TopSitesView and proxy some of its properties and signals, so it looks
> like it could be removed.
>
> On a related note, the naming of TopSitesView is slightly confusing, as it
> contains both top sites and bookmarks. A better name would probably be
> "NewTabPage" or "NewTabView".
Fixed on revision 568
- 569. By Arthur Mello
-
Simplify bookmarks transition
- 570. By Arthur Mello
-
Simplify start and end date calculation for timeframe history model
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> It looks like this could be simplified:
>
> 249 + start: {
> 250 + var date = new Date()
> 251 + date.setDate(
> 252 + date.setHours(0)
> 253 + date.setMinutes(0)
> 254 + date.setSeconds(0)
> 255 + date.setMillise
> 256 + return date
> 257 + }
> 258 + end: {
> 259 + var date = new Date()
> 260 + date.setDate(
> 261 + date.setHours(23)
> 262 + date.setMinutes(59)
> 263 + date.setSeconds(59)
> 264 + date.setMillise
> 265 + return date
> 266 + }
>
> For start, you don’t need to call setHours, setMinutes, setSeconds and
> setMilliseconds.
> For end, you can just return a new Date(), i.e.: "end: new Date()"
Fixed on revision 570
- 571. By Arthur Mello
-
Make NewTabView be loaded on demand using a Loader
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> As already discussed, in the current implementation TopSitesSheet is always
> instantiated. Instead, it should be loaded on demand (encapsulating it in a
> Component, or using a Loader), only when the user requests a new tab. It
> should probably be destroyed as soon as the URL of the current webview becomes
> non empty, and instantiated again when switching to a webview with an empty
> URL.
Fixed on revision 571
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> As already discussed, in the current implementation TopSitesSheet is always
> instantiated. Instead, it should be loaded on demand (encapsulating it in a
> Component, or using a Loader), only when the user requests a new tab. It
> should probably be destroyed as soon as the URL of the current webview becomes
> non empty, and instantiated again when switching to a webview with an empty
> URL.
Fixed on revision 571
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:565
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:571
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 572. By Arthur Mello
-
Fix AP tests. Removed the validation for webview visible after closing Activity View.
Sometimes, the New Tab view will be visible after Activity View and not the webview
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:572
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
There is a trivial conflict in tests/autopilot
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
For animations and behaviours, you’re using a few NumberAnimation with explicit durations. Please consider using UbuntuNumberAni
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
If I open a new empty tab, then type in a URL in the address bar and validate, I’m getting the following warning:
WARNING: Trying to pop an empty PageStack. Ignoring.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
The "see more/see less" transition is looking better, but still not perfect IMHO.
When I click "see more", I’m seeing the "top sites" section being abruptly shifted downwards, then fade away. It should probably move downwards smoothly and fade away at the same time.
When I click "see less", the extra bookmarks disappear instantly. Ideally they should fade away progressively as the footer is moved upwards.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Is there a need for the newTabViewVisible property? If so, couldn’t it just be defined as "newTabViewLoad
- 573. By Arthur Mello
-
Merge with trunk
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> There is a trivial conflict in
> tests/autopilot
> latest trunk, can you please resolve it?
Fixed on revision 573
- 574. By Arthur Mello
-
Using UbuntuNumberAni
mation instead of NumberAnimation
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> For animations and behaviours, you’re using a few NumberAnimation with
> explicit durations. Please consider using UbuntuNumberAni
> per.ubuntu.
> instead.
Fixed on revision 574
- 575. By Arthur Mello
-
Move newTabViewLoader to be a child of webviewContainer and stop using pageStack
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> If I open a new empty tab, then type in a URL in the address bar and validate,
> I’m getting the following warning:
>
> WARNING: Trying to pop an empty PageStack. Ignoring.
Fixed on revision 575
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:573
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> Is there a need for the newTabViewVisible property? If so, couldn’t it just be
> defined as "newTabViewLoad
The property was removed since it is not more nece
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> The "see more/see less" transition is looking better, but still not perfect
> IMHO.
>
> When I click "see more", I’m seeing the "top sites" section being abruptly
> shifted downwards, then fade away. It should probably move downwards smoothly
> and fade away at the same time.
The "bookmarks" and the "top sites" lists are inside a ListView. I tried to animate with Behavior on Y the delegate but with no success. My idea was to animate the move from the "top sites" component. Any suggestion?
> When I click "see less", the extra bookmarks disappear instantly. Ideally they
> should fade away progressively as the footer is moved upwards.
Right now the bookmarks list is a Repeater, not a ListView, inside a Column. I am animating the move transition of the Column. That is why we have the effect when we are "expanding" the Repeater. But to use the same effect before removing the entries of the Repeater I would need to use something like the delayRemove from the ListView. I didn't find a way to do that on a Repeater. Any suggestion? Should I change the Repeater to a ListView?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:575
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
114 + onUrlChanged: {
119 + function loadNewTabView() {
125 + function unloadNewTabView() {
There is a more declarative way of writing this: have a Component that contains a NewTabView inside the loader, and set the 'sourceComponent' property of the loader to "parent.url ? undefined : newTabViewCompo
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Regarding the transitions/
I’m seeing a new issue though (or maybe it was there already and I hadn’t spotted it before): on desktop, if I have more than 5 bookmarks and more than 5 top sites, only the first row of the top sites category can be seen, I can see the top of the second row but I cannot drag it into view, it always springs back to the first row.
- 576. By Arthur Mello
-
Merge with trunk
- 577. By Arthur Mello
-
Improve code for newTabView loader
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> 114 + onUrlChanged: {
> 119 + function loadNewTabView() {
> 125 + function unloadNewTabView() {
>
> There is a more declarative way of writing this: have a Component that
> contains a NewTabView inside the loader, and set the 'sourceComponent'
> property of the loader to "parent.url ? undefined : newTabViewCompo
Fixed on revision 577
- 578. By Arthur Mello
-
Stop changing the height property of the topSites Flow since that results on strange behavior
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> Regarding the transitions/
> do for now, we can tweak them when we implement the new design.
>
> I’m seeing a new issue though (or maybe it was there already and I hadn’t
> spotted it before): on desktop, if I have more than 5 bookmarks and more than
> 5 top sites, only the first row of the top sites category can be seen, I can
> see the top of the second row but I cannot drag it into view, it always
> springs back to the first row.
Fixed on revision 578
- 579. By Arthur Mello
-
Move newTabView inside a Component
- 580. By Arthur Mello
-
Fix url check to see if should load newTabComponent
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:577
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:580
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
It looks like the autopilot tests need updating, see https:/
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Huh, nevermind that last comment, I was looking at a previous CI run.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
A tiny remark on semantics:
943 + def ensure_
947 + def ensure_
Those two methods should be renamed:
assert_
assert_
Methods that start with "ensure_" actually perform some actions to ensure that the result is achieved, as opposed to methods that start with "assert_" that merely wait until the condition is verified.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
971 + def test_hidden_
I would rename the above method "test_new_
We could do with more autopilot tests (opening a bookmarked site, opening a top visited site), but this doesn’t have to be implemented now.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
1197 + void shouldLimitEntr
1198 + {
1199 + model->setLimit(2);
1200 +
1201 + history->add(QUrl("http://
1202 + QTest::qWait(1001);
1203 + history->add(QUrl("http://
1204 + QTest::qWait(1001);
1205 + history->add(QUrl("http://
1206 +
1207 + QCOMPARE(
1208 + QCOMPARE(
1209 + }
It would be useful to test additional conditions:
- checking that the initial value of the limit is -1
- setting the limit to -1
- setting the limit to a number greater than the number of entries
- setting the limit after populating the history model, to verify that it is correctly updated
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Can you add a comment to state that LimitProxyModel was copied from unity8’s source tree?
Did you have to modify it, or is it a verbatim copy?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
386 + property alias favIcon: favIcon.source
387 + property alias url: url.text
388 + property alias label: label.text
Can the 'favIcon' property be renamed 'icon', for consistency across the codebase?
Can the 'label' property be renamed 'title', to better express what it contains?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
36 + width: parent.width
this property shouldn’t be defined here in the component, but rather at the place in the code where it is instantiated, i.e.:
BookmarksList {
width: parent.width
}
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
223 + onVisibleChanged: {
224 + if (visible)
225 + updateSectionsD
226 + }
Is this really needed? I have the impression that now that the view is instantiated and destroyed on demand, it could be removed.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
> Is this really needed? I have the impression that now that the view is
> instantiated and destroyed on demand, it could be removed.
Well, granted: sectionsModel would need to be populated when instantiating the view, but it could be done statically inside the ListModel instead of dynamically.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
209 + property int bookmarksCountL
210 + property bool seeMoreBookmark
It looks like those two properties don’t need to be exposed publicly, as they’re not being used anywhere outside the component. Can they be made private, by moving them to a QtObject instance with id 'private'?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Can you please update the translations template?
cmake .
make webbrowser-app.pot
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
Apart from the handful of minor remarks above, the functionality looks good and works fine, so once all the comments are addressed, this will be ok to merge.
- 581. By Arthur Mello
-
Change AP test methods names for semantics reasons
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> A tiny remark on semantics:
>
> 943 + def ensure_
> 947 + def ensure_
>
> Those two methods should be renamed:
>
> assert_
> assert_
>
> Methods that start with "ensure_" actually perform some actions to ensure that
> the result is achieved, as opposed to methods that start with "assert_" that
> merely wait until the condition is verified.
Fixed on revision 581
- 582. By Arthur Mello
-
Rename AP method
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> 971 + def test_hidden_
>
> I would rename the above method "test_new_
>
> We could do with more autopilot tests (opening a bookmarked site, opening a
> top visited site), but this doesn’t have to be implemented now.
Fixed on revision 582
- 583. By Arthur Mello
-
Add additional conditions to Limit Proxy Model unittest
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> 1197 + void shouldLimitEntr
> 1198 + {
> 1199 + model->setLimit(2);
> 1200 +
> 1201 + history->add(QUrl("http://
> Domain", QUrl());
> 1202 + QTest::qWait(1001);
> 1203 + history->add(QUrl("http://
> Domain", QUrl());
> 1204 + QTest::qWait(1001);
> 1205 + history->add(QUrl("http://
> Domain", QUrl());
> 1206 +
> 1207 + QCOMPARE(
> 1208 + QCOMPARE(
> 1209 + }
>
> It would be useful to test additional conditions:
> - checking that the initial value of the limit is -1
> - setting the limit to -1
> - setting the limit to a number greater than the number of entries
> - setting the limit after populating the history model, to verify that it is
> correctly updated
Fixed on revision 583
- 584. By Arthur Mello
-
State that LimitProxyModel was copied from unity8’s source tree with small changes
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> Can you add a comment to state that LimitProxyModel was copied from unity8’s
> source tree?
> Did you have to modify it, or is it a verbatim copy?
Fixed on revision 584
- 585. By Arthur Mello
-
Rename UrlDelegate properties
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> 386 + property alias favIcon: favIcon.source
> 387 + property alias url: url.text
> 388 + property alias label: label.text
>
> Can the 'favIcon' property be renamed 'icon', for consistency across the
> codebase?
> Can the 'label' property be renamed 'title', to better express what it
> contains?
Fixed on revision 585
- 586. By Arthur Mello
-
Fix width definition of component
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> 36 + width: parent.width
>
> this property shouldn’t be defined here in the component, but rather at the
> place in the code where it is instantiated, i.e.:
>
> BookmarksList {
> width: parent.width
> }
Fixed on revision 586
- 587. By Arthur Mello
-
Populate sectionsMode statically
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> > Is this really needed? I have the impression that now that the view is
> > instantiated and destroyed on demand, it could be removed.
>
> Well, granted: sectionsModel would need to be populated when instantiating the
> view, but it could be done statically inside the ListModel instead of
> dynamically.
Fixed on revision 587
- 588. By Arthur Mello
-
Change newTabView properties to private
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> 209 + property int bookmarksCountL
> 210 + property bool seeMoreBookmark
>
> It looks like those two properties don’t need to be exposed publicly, as
> they’re not being used anywhere outside the component. Can they be made
> private, by moving them to a QtObject instance with id 'private'?
Fixed on revision 588
- 589. By Arthur Mello
-
Fix AP test
- 590. By Arthur Mello
-
Merge with trunk
- 591. By Arthur Mello
-
update the translations template
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Arthur Mello (artmello) wrote : | # |
> Can you please update the translations template?
>
> cmake .
> make webbrowser-app.pot
Fixed on revision 591
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
319 + end: new Date()
This is actually not needed at all, if no 'end' marker is specified, the timeframe will be open on this side, which suits our use case.
- 592. By Arthur Mello
-
Rmeove unnecessary code
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Olivier Tilloy (osomon) wrote : | # |
That looks good now. Thanks for your work on this!
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:582
http://
Executed test runs:
UNSTABLE: http://
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Preview Diff
1 | === modified file 'po/webbrowser-app.pot' |
2 | --- po/webbrowser-app.pot 2014-06-25 16:34:08 +0000 |
3 | +++ po/webbrowser-app.pot 2014-07-04 14:19:02 +0000 |
4 | @@ -8,7 +8,7 @@ |
5 | msgstr "" |
6 | "Project-Id-Version: webbrowser-app\n" |
7 | "Report-Msgid-Bugs-To: \n" |
8 | -"POT-Creation-Date: 2014-06-25 17:33+0100\n" |
9 | +"POT-Creation-Date: 2014-07-04 10:06-0300\n" |
10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
11 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
12 | "Language-Team: LANGUAGE <LL@li.org>\n" |
13 | @@ -81,15 +81,15 @@ |
14 | msgid "Back to safety" |
15 | msgstr "" |
16 | |
17 | -#: src/app/Chrome.qml:75 src/app/actions/Back.qml:23 |
18 | +#: src/app/Chrome.qml:76 src/app/actions/Back.qml:23 |
19 | msgid "Back" |
20 | msgstr "" |
21 | |
22 | -#: src/app/Chrome.qml:94 src/app/actions/Forward.qml:23 |
23 | +#: src/app/Chrome.qml:95 src/app/actions/Forward.qml:23 |
24 | msgid "Forward" |
25 | msgstr "" |
26 | |
27 | -#: src/app/Chrome.qml:157 src/app/webbrowser/ActivityView.qml:36 |
28 | +#: src/app/Chrome.qml:158 src/app/webbrowser/ActivityView.qml:36 |
29 | msgid "Activity" |
30 | msgstr "" |
31 | |
32 | @@ -249,9 +249,22 @@ |
33 | msgstr "" |
34 | |
35 | #: src/app/webbrowser/ActivityView.qml:54 |
36 | +#: src/app/webbrowser/NewTabView.qml:123 |
37 | msgid "Bookmarks" |
38 | msgstr "" |
39 | |
40 | +#: src/app/webbrowser/NewTabView.qml:125 |
41 | +msgid "Top sites" |
42 | +msgstr "" |
43 | + |
44 | +#: src/app/webbrowser/NewTabView.qml:142 |
45 | +msgid "see less" |
46 | +msgstr "" |
47 | + |
48 | +#: src/app/webbrowser/NewTabView.qml:142 |
49 | +msgid "see more" |
50 | +msgstr "" |
51 | + |
52 | #. TRANSLATORS: %1 refers to the number of open tabs |
53 | #: src/app/webbrowser/TabsList.qml:37 |
54 | #, qt-format |
55 | @@ -307,13 +320,13 @@ |
56 | msgstr "" |
57 | |
58 | #. TRANSLATORS: %1 refers to the current page’s title |
59 | -#: src/app/webbrowser/webbrowser-app.qml:35 |
60 | +#: src/app/webbrowser/webbrowser-app.qml:36 |
61 | #: src/app/webcontainer/webapp-container.qml:54 |
62 | #, qt-format |
63 | msgid "%1 - Ubuntu Web Browser" |
64 | msgstr "" |
65 | |
66 | -#: src/app/webbrowser/webbrowser-app.qml:37 |
67 | +#: src/app/webbrowser/webbrowser-app.qml:38 |
68 | #: src/app/webcontainer/webapp-container.qml:56 |
69 | msgid "Ubuntu Web Browser" |
70 | msgstr "" |
71 | |
72 | === added file 'src/app/webbrowser/BookmarksList.qml' |
73 | --- src/app/webbrowser/BookmarksList.qml 1970-01-01 00:00:00 +0000 |
74 | +++ src/app/webbrowser/BookmarksList.qml 2014-07-04 14:19:02 +0000 |
75 | @@ -0,0 +1,75 @@ |
76 | +/* |
77 | + * Copyright 2014 Canonical Ltd. |
78 | + * |
79 | + * This file is part of webbrowser-app. |
80 | + * |
81 | + * webbrowser-app is free software; you can redistribute it and/or modify |
82 | + * it under the terms of the GNU General Public License as published by |
83 | + * the Free Software Foundation; version 3. |
84 | + * |
85 | + * webbrowser-app is distributed in the hope that it will be useful, |
86 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
87 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
88 | + * GNU General Public License for more details. |
89 | + * |
90 | + * You should have received a copy of the GNU General Public License |
91 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
92 | + */ |
93 | + |
94 | +import QtQuick 2.0 |
95 | +import Ubuntu.Components 0.1 |
96 | + |
97 | +Column { |
98 | + id: bookmarksList |
99 | + |
100 | + property alias model: bookmarksListRepeater.model |
101 | + property alias footerLabelText: footerLabel.text |
102 | + property alias footerLabelVisible: footerLabel.visible |
103 | + |
104 | + signal bookmarkClicked(url url) |
105 | + signal footerLabelClicked() |
106 | + |
107 | + spacing: units.gu(1) |
108 | + |
109 | + move: Transition { UbuntuNumberAnimation { properties: "x, y" } } |
110 | + |
111 | + Repeater { |
112 | + id: bookmarksListRepeater |
113 | + |
114 | + delegate: UrlDelegate{ |
115 | + width: bookmarksList.width |
116 | + height: units.gu(5) |
117 | + |
118 | + icon: model.icon |
119 | + title: model.title ? model.title : model.url |
120 | + url: model.url |
121 | + |
122 | + onClicked: bookmarkClicked(model.url) |
123 | + } |
124 | + } |
125 | + |
126 | + Rectangle { |
127 | + width: parent.width |
128 | + height: footerLabel.visible ? footerLabel.height + units.gu(6) : 0 |
129 | + |
130 | + MouseArea { |
131 | + anchors.centerIn: footerLabel |
132 | + |
133 | + width: footerLabel.width + units.gu(4) |
134 | + height: footerLabel.height + units.gu(4) |
135 | + |
136 | + enabled: footerLabel.visible |
137 | + |
138 | + onClicked: footerLabelClicked() |
139 | + } |
140 | + |
141 | + Label { |
142 | + id: footerLabel |
143 | + anchors.centerIn: parent |
144 | + |
145 | + visible: true |
146 | + |
147 | + font.bold: true |
148 | + } |
149 | + } |
150 | +} |
151 | |
152 | === modified file 'src/app/webbrowser/Browser.qml' |
153 | --- src/app/webbrowser/Browser.qml 2014-06-30 10:40:59 +0000 |
154 | +++ src/app/webbrowser/Browser.qml 2014-07-04 14:19:02 +0000 |
155 | @@ -48,7 +48,7 @@ |
156 | }, |
157 | Actions.Bookmark { |
158 | enabled: currentWebview |
159 | - onTriggered: bookmarksModel.add(currentWebview.url, currentWebview.title, "")//currentWebview.icon) |
160 | + onTriggered: _bookmarksModel.add(currentWebview.url, currentWebview.title, "")//currentWebview.icon) |
161 | }, |
162 | Actions.NewTab { |
163 | onTriggered: openUrlInNewTab("", true) |
164 | @@ -118,6 +118,11 @@ |
165 | currentWebview.url = url |
166 | toggleActivityView() |
167 | } |
168 | + |
169 | + function onNewTabUrlRequested(url) { |
170 | + currentWebview.url = url |
171 | + currentWebview.forceActiveFocus() |
172 | + } |
173 | } |
174 | |
175 | readonly property bool activityViewVisible: stack.depth > 0 |
176 | @@ -126,7 +131,7 @@ |
177 | stack.push(Qt.resolvedUrl("ActivityView.qml"), |
178 | {tabsModel: tabsModel, |
179 | historyModel: _historyModel, |
180 | - bookmarksModel: bookmarksModel}) |
181 | + bookmarksModel: _bookmarksModel}) |
182 | var view = stack.currentPage |
183 | view.onHistoryEntryRequested.connect(internal.onHistoryEntryRequested) |
184 | view.onNewTabRequested.connect(internal.onNewTabRequested) |
185 | @@ -209,7 +214,7 @@ |
186 | } |
187 | |
188 | BookmarksModel { |
189 | - id: bookmarksModel |
190 | + id: _bookmarksModel |
191 | databasePath: dataLocation + "/bookmarks.sqlite" |
192 | } |
193 | |
194 | @@ -236,7 +241,7 @@ |
195 | } |
196 | Actions.BookmarkLink { |
197 | enabled: contextualData.href.toString() |
198 | - onTriggered: bookmarksModel.add(contextualData.href, contextualData.title, "") |
199 | + onTriggered: _bookmarksModel.add(contextualData.href, contextualData.title, "") |
200 | } |
201 | Actions.CopyLink { |
202 | enabled: contextualData.href.toString() |
203 | @@ -276,6 +281,26 @@ |
204 | visible = Qt.binding(function() { return current }) |
205 | } |
206 | } |
207 | + |
208 | + Loader { |
209 | + id: newTabViewLoader |
210 | + anchors.fill: parent |
211 | + |
212 | + sourceComponent: !parent.url.toString() ? newTabViewComponent : undefined |
213 | + |
214 | + Component { |
215 | + id: newTabViewComponent |
216 | + |
217 | + NewTabView { |
218 | + anchors.fill: parent |
219 | + |
220 | + historyModel: _historyModel |
221 | + bookmarksModel: _bookmarksModel |
222 | + onBookmarkClicked: internal.onNewTabUrlRequested(url) |
223 | + onHistoryEntryClicked: internal.onNewTabUrlRequested(url) |
224 | + } |
225 | + } |
226 | + } |
227 | } |
228 | } |
229 | |
230 | |
231 | === modified file 'src/app/webbrowser/CMakeLists.txt' |
232 | --- src/app/webbrowser/CMakeLists.txt 2014-06-30 10:40:59 +0000 |
233 | +++ src/app/webbrowser/CMakeLists.txt 2014-07-04 14:19:02 +0000 |
234 | @@ -13,7 +13,9 @@ |
235 | history-domainlist-model.cpp |
236 | history-matches-model.cpp |
237 | history-model.cpp |
238 | + history-byvisits-model.cpp |
239 | history-timeframe-model.cpp |
240 | + limit-proxy-model.cpp |
241 | tabs-model.cpp |
242 | ) |
243 | |
244 | |
245 | === added file 'src/app/webbrowser/NewTabView.qml' |
246 | --- src/app/webbrowser/NewTabView.qml 1970-01-01 00:00:00 +0000 |
247 | +++ src/app/webbrowser/NewTabView.qml 2014-07-04 14:19:02 +0000 |
248 | @@ -0,0 +1,176 @@ |
249 | +/* |
250 | + * Copyright 2014 Canonical Ltd. |
251 | + * |
252 | + * This file is part of webbrowser-app. |
253 | + * |
254 | + * webbrowser-app is free software; you can redistribute it and/or modify |
255 | + * it under the terms of the GNU General Public License as published by |
256 | + * the Free Software Foundation; version 3. |
257 | + * |
258 | + * webbrowser-app is distributed in the hope that it will be useful, |
259 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
260 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
261 | + * GNU General Public License for more details. |
262 | + * |
263 | + * You should have received a copy of the GNU General Public License |
264 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
265 | + */ |
266 | + |
267 | +import QtQuick 2.0 |
268 | +import Ubuntu.Components 0.1 |
269 | +import Ubuntu.Components.ListItems 0.1 as ListItem |
270 | +import webbrowserapp.private 0.1 |
271 | + |
272 | +Item { |
273 | + id: newTabView |
274 | + |
275 | + property QtObject bookmarksModel |
276 | + property QtObject historyModel |
277 | + |
278 | + signal bookmarkClicked(url url) |
279 | + signal historyEntryClicked(url url) |
280 | + |
281 | + QtObject { |
282 | + id: internal |
283 | + |
284 | + property int bookmarksCountLimit: 5 |
285 | + property bool seeMoreBookmarksView: false |
286 | + } |
287 | + |
288 | + ListModel { |
289 | + id: sectionsModel |
290 | + |
291 | + Component.onCompleted: { |
292 | + if (bookmarksListModel && bookmarksListModel.count !== 0) |
293 | + sectionsModel.append({ section: "bookmarks" }); |
294 | + if (historyListModel && historyListModel.count !== 0 && !internal.seeMoreBookmarksView ) |
295 | + sectionsModel.append({ section: "topsites" }); |
296 | + } |
297 | + } |
298 | + |
299 | + LimitProxyModel { |
300 | + id: bookmarksListModel |
301 | + |
302 | + sourceModel: newTabView.bookmarksModel |
303 | + |
304 | + limit: internal.seeMoreBookmarksView ? -1 : internal.bookmarksCountLimit |
305 | + } |
306 | + |
307 | + LimitProxyModel { |
308 | + id: historyListModel |
309 | + |
310 | + sourceModel: HistoryByVisitsModel { |
311 | + sourceModel: HistoryTimeframeModel { |
312 | + sourceModel: newTabView.historyModel |
313 | + // We only show sites visited on the last 60 days |
314 | + start: { |
315 | + var date = new Date() |
316 | + date.setDate(date.getDate() - 60) |
317 | + return date |
318 | + } |
319 | + } |
320 | + } |
321 | + |
322 | + limit: 10 |
323 | + } |
324 | + |
325 | + Rectangle { |
326 | + id: newTabBackground |
327 | + anchors.fill: parent |
328 | + color: "white" |
329 | + } |
330 | + |
331 | + ListView { |
332 | + id: newTabListView |
333 | + anchors.fill: parent |
334 | + |
335 | + model: sectionsModel |
336 | + |
337 | + delegate: Loader { |
338 | + anchors { |
339 | + left: parent.left |
340 | + right: parent.right |
341 | + margins: units.gu(2) |
342 | + } |
343 | + |
344 | + width: parent.width |
345 | + height: children.height |
346 | + |
347 | + sourceComponent: modelData == "bookmarks" ? bookmarksComponent : topSitesComponent |
348 | + } |
349 | + |
350 | + section.property: "section" |
351 | + section.delegate: Rectangle { |
352 | + anchors { |
353 | + left: parent.left |
354 | + right: parent.right |
355 | + } |
356 | + |
357 | + height: sectionHeader.height + units.gu(1) |
358 | + |
359 | + opacity: section == "topsites" && internal.seeMoreBookmarksView ? 0.0 : 1.0 |
360 | + |
361 | + color: newTabBackground.color |
362 | + |
363 | + Behavior on opacity { UbuntuNumberAnimation {} } |
364 | + |
365 | + ListItem.Header { |
366 | + id: sectionHeader |
367 | + |
368 | + text: { |
369 | + if (section == "bookmarks") { |
370 | + return i18n.tr("Bookmarks") |
371 | + } else if (section == "topsites") { |
372 | + return i18n.tr("Top sites") |
373 | + } |
374 | + } |
375 | + } |
376 | + } |
377 | + |
378 | + section.labelPositioning: ViewSection.InlineLabels | ViewSection.CurrentLabelAtStart |
379 | + } |
380 | + |
381 | + Component { |
382 | + id: bookmarksComponent |
383 | + |
384 | + BookmarksList { |
385 | + width: parent.width |
386 | + |
387 | + model: bookmarksListModel |
388 | + |
389 | + footerLabelText: internal.seeMoreBookmarksView ? i18n.tr("see less") : i18n.tr("see more") |
390 | + footerLabelVisible: bookmarksListModel.unlimitedCount > internal.bookmarksCountLimit |
391 | + |
392 | + onBookmarkClicked: newTabView.bookmarkClicked(url) |
393 | + onFooterLabelClicked: internal.seeMoreBookmarksView = !internal.seeMoreBookmarksView |
394 | + } |
395 | + } |
396 | + |
397 | + Component { |
398 | + id: topSitesComponent |
399 | + |
400 | + Flow { |
401 | + width: parent.width |
402 | + |
403 | + spacing: units.gu(1) |
404 | + |
405 | + opacity: internal.seeMoreBookmarksView ? 0.0 : 1.0 |
406 | + |
407 | + Behavior on opacity { UbuntuNumberAnimation {} } |
408 | + |
409 | + Repeater { |
410 | + model: parent.opacity == 0.0 ? "" : historyListModel |
411 | + |
412 | + delegate: PageDelegate{ |
413 | + width: units.gu(18) |
414 | + height: units.gu(25) |
415 | + |
416 | + url: model.url |
417 | + label: model.title ? model.title : model.url |
418 | + |
419 | + onClicked: historyEntryClicked(model.url) |
420 | + } |
421 | + } |
422 | + } |
423 | + } |
424 | +} |
425 | |
426 | === added file 'src/app/webbrowser/UrlDelegate.qml' |
427 | --- src/app/webbrowser/UrlDelegate.qml 1970-01-01 00:00:00 +0000 |
428 | +++ src/app/webbrowser/UrlDelegate.qml 2014-07-04 14:19:02 +0000 |
429 | @@ -0,0 +1,73 @@ |
430 | +/* |
431 | + * Copyright 2014 Canonical Ltd. |
432 | + * |
433 | + * This file is part of webbrowser-app. |
434 | + * |
435 | + * webbrowser-app is free software; you can redistribute it and/or modify |
436 | + * it under the terms of the GNU General Public License as published by |
437 | + * the Free Software Foundation; version 3. |
438 | + * |
439 | + * webbrowser-app is distributed in the hope that it will be useful, |
440 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
441 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
442 | + * GNU General Public License for more details. |
443 | + * |
444 | + * You should have received a copy of the GNU General Public License |
445 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
446 | + */ |
447 | + |
448 | +import QtQuick 2.0 |
449 | +import Ubuntu.Components 0.1 |
450 | + |
451 | +Item { |
452 | + id: urlDelegate |
453 | + |
454 | + property alias icon: icon.source |
455 | + property alias url: url.text |
456 | + property alias title: title.text |
457 | + |
458 | + signal clicked() |
459 | + |
460 | + MouseArea { |
461 | + anchors.fill: parent |
462 | + onClicked: urlDelegate.clicked() |
463 | + } |
464 | + |
465 | + Row { |
466 | + anchors.fill: parent |
467 | + spacing: units.gu(1) |
468 | + |
469 | + UbuntuShape { |
470 | + id: iconShape |
471 | + height: parent.height |
472 | + width: parent.height |
473 | + |
474 | + image: Image { |
475 | + id: icon |
476 | + } |
477 | + } |
478 | + |
479 | + Column { |
480 | + width: parent.width - iconShape.width - spacing |
481 | + Label { |
482 | + id: title |
483 | + width: parent.width |
484 | + font.bold: true |
485 | + |
486 | + wrapMode: Text.Wrap |
487 | + elide: Text.ElideRight |
488 | + maximumLineCount: 1 |
489 | + } |
490 | + |
491 | + Label { |
492 | + id: url |
493 | + width: parent.width |
494 | + fontSize: "small" |
495 | + |
496 | + wrapMode: Text.Wrap |
497 | + elide: Text.ElideRight |
498 | + maximumLineCount: 1 |
499 | + } |
500 | + } |
501 | + } |
502 | +} |
503 | |
504 | === added file 'src/app/webbrowser/history-byvisits-model.cpp' |
505 | --- src/app/webbrowser/history-byvisits-model.cpp 1970-01-01 00:00:00 +0000 |
506 | +++ src/app/webbrowser/history-byvisits-model.cpp 2014-07-04 14:19:02 +0000 |
507 | @@ -0,0 +1,50 @@ |
508 | +/* |
509 | + * Copyright 2014 Canonical Ltd. |
510 | + * |
511 | + * This file is part of webbrowser-app. |
512 | + * |
513 | + * webbrowser-app is free software; you can redistribute it and/or modify |
514 | + * it under the terms of the GNU General Public License as published by |
515 | + * the Free Software Foundation; version 3. |
516 | + * |
517 | + * webbrowser-app is distributed in the hope that it will be useful, |
518 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
519 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
520 | + * GNU General Public License for more details. |
521 | + * |
522 | + * You should have received a copy of the GNU General Public License |
523 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
524 | + */ |
525 | + |
526 | +#include "history-byvisits-model.h" |
527 | +#include "history-timeframe-model.h" |
528 | +#include "history-model.h" |
529 | + |
530 | +/*! |
531 | + \class HistoryByVisitsModel |
532 | + \brief Proxy model that sorts a history model by number of visits |
533 | + |
534 | + HistoryByVisitsModel is a proxy model that sorts a |
535 | + HistoryTimeframeModel by the number of visits |
536 | + (i.e. the history with the greatest number of visits first). |
537 | +*/ |
538 | +HistoryByVisitsModel::HistoryByVisitsModel(QObject* parent) |
539 | + : QSortFilterProxyModel(parent) |
540 | +{ |
541 | + setDynamicSortFilter(true); |
542 | + setSortRole(HistoryModel::Visits); |
543 | + sort(0, Qt::DescendingOrder); |
544 | +} |
545 | + |
546 | +HistoryTimeframeModel* HistoryByVisitsModel::sourceModel() const |
547 | +{ |
548 | + return qobject_cast<HistoryTimeframeModel*>(QSortFilterProxyModel::sourceModel()); |
549 | +} |
550 | + |
551 | +void HistoryByVisitsModel::setSourceModel(HistoryTimeframeModel* sourceModel) |
552 | +{ |
553 | + if (sourceModel != this->sourceModel()) { |
554 | + QSortFilterProxyModel::setSourceModel(sourceModel); |
555 | + Q_EMIT sourceModelChanged(); |
556 | + } |
557 | +} |
558 | |
559 | === added file 'src/app/webbrowser/history-byvisits-model.h' |
560 | --- src/app/webbrowser/history-byvisits-model.h 1970-01-01 00:00:00 +0000 |
561 | +++ src/app/webbrowser/history-byvisits-model.h 2014-07-04 14:19:02 +0000 |
562 | @@ -0,0 +1,43 @@ |
563 | +/* |
564 | + * Copyright 2014 Canonical Ltd. |
565 | + * |
566 | + * This file is part of webbrowser-app. |
567 | + * |
568 | + * webbrowser-app is free software; you can redistribute it and/or modify |
569 | + * it under the terms of the GNU General Public License as published by |
570 | + * the Free Software Foundation; version 3. |
571 | + * |
572 | + * webbrowser-app is distributed in the hope that it will be useful, |
573 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
574 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
575 | + * GNU General Public License for more details. |
576 | + * |
577 | + * You should have received a copy of the GNU General Public License |
578 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
579 | + */ |
580 | + |
581 | +#ifndef __HISTORY_BYVISITS_MODEL_H__ |
582 | +#define __HISTORY_BYVISITS_MODEL_H__ |
583 | + |
584 | +// Qt |
585 | +#include <QtCore/QSortFilterProxyModel> |
586 | + |
587 | +class HistoryTimeframeModel; |
588 | + |
589 | +class HistoryByVisitsModel : public QSortFilterProxyModel |
590 | +{ |
591 | + Q_OBJECT |
592 | + |
593 | + Q_PROPERTY(HistoryTimeframeModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged) |
594 | + |
595 | +public: |
596 | + HistoryByVisitsModel(QObject* parent=0); |
597 | + |
598 | + HistoryTimeframeModel* sourceModel() const; |
599 | + void setSourceModel(HistoryTimeframeModel* sourceModel); |
600 | + |
601 | +Q_SIGNALS: |
602 | + void sourceModelChanged() const; |
603 | +}; |
604 | + |
605 | +#endif // __HISTORY_BYVISITS_MODEL_H__ |
606 | |
607 | === added file 'src/app/webbrowser/limit-proxy-model.cpp' |
608 | --- src/app/webbrowser/limit-proxy-model.cpp 1970-01-01 00:00:00 +0000 |
609 | +++ src/app/webbrowser/limit-proxy-model.cpp 2014-07-04 14:19:02 +0000 |
610 | @@ -0,0 +1,268 @@ |
611 | +/* |
612 | + * Copyright 2014 Canonical Ltd. |
613 | + * |
614 | + * This file is part of webbrowser-app. |
615 | + * |
616 | + * webbrowser-app is free software; you can redistribute it and/or modify |
617 | + * it under the terms of the GNU General Public License as published by |
618 | + * the Free Software Foundation; version 3. |
619 | + * |
620 | + * webbrowser-app is distributed in the hope that it will be useful, |
621 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
622 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
623 | + * GNU General Public License for more details. |
624 | + * |
625 | + * You should have received a copy of the GNU General Public License |
626 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
627 | + */ |
628 | + |
629 | +#include <QtCore/QAbstractItemModel> |
630 | + |
631 | +#include "limit-proxy-model.h" |
632 | + |
633 | +/*! |
634 | + \class LimitProxyModel |
635 | + \brief Identity model that limits the number of rows returned by a model |
636 | + |
637 | + LimitProxyModel is a proxy model that limits the number |
638 | + of rows returned by a model |
639 | + (i.e. only the first N entries are returned). |
640 | + |
641 | + This proxy model was copied from unity8's source tree with small changes, |
642 | + like the type of the sourceModel. |
643 | +*/ |
644 | +LimitProxyModel::LimitProxyModel(QObject* parent) |
645 | + : QIdentityProxyModel(parent), |
646 | + m_limit(-1), |
647 | + m_sourceInserting(false), |
648 | + m_sourceRemoving(false), |
649 | + m_dataChangedBegin(-1), |
650 | + m_dataChangedEnd(-1) |
651 | + |
652 | +{ |
653 | + connect(this, SIGNAL(modelReset()), SIGNAL(countChanged())); |
654 | + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged())); |
655 | + connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(countChanged())); |
656 | + connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(unlimitedCountChanged())); |
657 | + connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(unlimitedCountChanged())); |
658 | +} |
659 | + |
660 | +QAbstractItemModel* LimitProxyModel::sourceModel() const |
661 | +{ |
662 | + return qobject_cast<QAbstractItemModel*>(QIdentityProxyModel::sourceModel()); |
663 | +} |
664 | + |
665 | +void LimitProxyModel::setSourceModel(QObject* sourceModel) |
666 | +{ |
667 | + QAbstractItemModel* model = qobject_cast<QAbstractItemModel*>(sourceModel); |
668 | + |
669 | + if (model != this->sourceModel()) { |
670 | + if (this->sourceModel() != NULL) { |
671 | + this->sourceModel()->disconnect(this); |
672 | + } |
673 | + |
674 | + QIdentityProxyModel::setSourceModel(model); |
675 | + |
676 | + if (this->sourceModel() != NULL) { |
677 | + // Disconnect the QIdentityProxyModel handling for rows removed/added... |
678 | + disconnect(this->sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, NULL); |
679 | + disconnect(this->sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), this, NULL); |
680 | + disconnect(this->sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, NULL); |
681 | + disconnect(this->sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, NULL); |
682 | + |
683 | + // ... and use our own |
684 | + connect(this->sourceModel(), SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), |
685 | + this, SLOT(sourceRowsAboutToBeInserted(QModelIndex,int,int))); |
686 | + connect(this->sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), |
687 | + this, SLOT(sourceRowsInserted(QModelIndex,int,int))); |
688 | + connect(this->sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), |
689 | + this, SLOT(sourceRowsAboutToBeRemoved(QModelIndex,int,int))); |
690 | + connect(this->sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)), |
691 | + this, SLOT(sourceRowsRemoved(QModelIndex,int,int))); |
692 | + } |
693 | + Q_EMIT sourceModelChanged(); |
694 | + } |
695 | +} |
696 | + |
697 | +int LimitProxyModel::rowCount(const QModelIndex &parent) const |
698 | +{ |
699 | + if (parent.isValid()) // We are not a tree |
700 | + return 0; |
701 | + |
702 | + const int unlimitedCount = QIdentityProxyModel::rowCount(parent); |
703 | + return m_limit < 0 ? unlimitedCount : qMin(m_limit, unlimitedCount); |
704 | +} |
705 | + |
706 | +int LimitProxyModel::unlimitedRowCount(const QModelIndex &parent) const |
707 | +{ |
708 | + if (parent.isValid()) // We are not a tree |
709 | + return 0; |
710 | + |
711 | + return QIdentityProxyModel::rowCount(parent); |
712 | +} |
713 | + |
714 | +int LimitProxyModel::limit() const |
715 | +{ |
716 | + return m_limit; |
717 | +} |
718 | + |
719 | +void LimitProxyModel::setLimit(int limit) |
720 | +{ |
721 | + if (limit != m_limit) { |
722 | + bool inserting = false; |
723 | + bool removing = false; |
724 | + const int oldCount = rowCount(); |
725 | + const int unlimitedCount = QIdentityProxyModel::rowCount(); |
726 | + if (m_limit < 0) { |
727 | + if (limit < oldCount) { |
728 | + removing = true; |
729 | + beginRemoveRows(QModelIndex(), limit, oldCount - 1); |
730 | + } |
731 | + } else if (limit < 0) { |
732 | + if (m_limit < unlimitedCount) { |
733 | + inserting = true; |
734 | + beginInsertRows(QModelIndex(), m_limit, unlimitedCount - 1); |
735 | + } |
736 | + } else { |
737 | + if (limit > m_limit && unlimitedCount > m_limit) { |
738 | + inserting = true; |
739 | + beginInsertRows(QModelIndex(), m_limit, qMin(limit, unlimitedCount) - 1); |
740 | + } else if (limit < m_limit && limit < oldCount) { |
741 | + removing = true; |
742 | + beginRemoveRows(QModelIndex(), limit, oldCount - 1); |
743 | + } |
744 | + } |
745 | + |
746 | + m_limit = limit; |
747 | + |
748 | + if (inserting) { |
749 | + endInsertRows(); |
750 | + } else if (removing) { |
751 | + endRemoveRows(); |
752 | + } |
753 | + |
754 | + Q_EMIT limitChanged(); |
755 | + } |
756 | +} |
757 | + |
758 | +void LimitProxyModel::sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int end) |
759 | +{ |
760 | + if (m_limit < 0) { |
761 | + beginInsertRows(mapFromSource(parent), start, end); |
762 | + m_sourceInserting = true; |
763 | + } else if (start < m_limit) { |
764 | + const int nSourceAddedItems = end - start + 1; |
765 | + const int currentCount = QIdentityProxyModel::rowCount(); |
766 | + if (currentCount + nSourceAddedItems <= m_limit) { |
767 | + // After Inserting items we will be under the limit |
768 | + // so just proceed with the insertion normally |
769 | + beginInsertRows(mapFromSource(parent), start, end); |
770 | + m_sourceInserting = true; |
771 | + } else if (currentCount >= m_limit) { |
772 | + // We are already over the limit so to our users we are not adding items, just |
773 | + // changing it's data, i.e we had something like |
774 | + // A B C D E |
775 | + // with a limit of 5 |
776 | + // after inserting (let's say three 'F' at position 1) we will have |
777 | + // A F F F B |
778 | + // so we just need to signal a dataChanged from 1 to 4 |
779 | + m_dataChangedBegin = start; |
780 | + m_dataChangedEnd = m_limit - 1; |
781 | + } else { // currentCount < m_limit && currentCount + nSourceAddedItems > m_limit |
782 | + // We have less items than the limit but after adding them we will be over |
783 | + // To our users this means we need to insert some items and change the |
784 | + // data of some others, i.e we had something like |
785 | + // A B C |
786 | + // with a limit of 5 |
787 | + // after inserting (let's say three 'F' at position 1) we will have |
788 | + // A F F F B |
789 | + // so we need to signal an insetion from position 1 to 2, instead of from |
790 | + // position 1 to 3 and a after that a data changed from 3 to 4 |
791 | + const int nItemsToInsert = m_limit - currentCount; |
792 | + beginInsertRows(mapFromSource(parent), start, start + nItemsToInsert - 1); |
793 | + m_sourceInserting = true; |
794 | + m_dataChangedBegin = start + nItemsToInsert; |
795 | + m_dataChangedEnd = m_limit - 1; |
796 | + if (m_dataChangedBegin > m_dataChangedEnd) { |
797 | + // Just in case we were empty and insert 6 items with a limit of 5 |
798 | + // We don't want to signal a dataChanged from 5 to 4 |
799 | + m_dataChangedBegin = -1; |
800 | + m_dataChangedEnd = -1; |
801 | + } |
802 | + } |
803 | + } |
804 | +} |
805 | + |
806 | +void LimitProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) |
807 | +{ |
808 | + if (m_limit < 0) { |
809 | + beginRemoveRows(mapFromSource(parent), start, end); |
810 | + m_sourceRemoving = true; |
811 | + } else if (start < m_limit) { |
812 | + const int nSourceRemovedItems = end - start + 1; |
813 | + const int currentCount = QIdentityProxyModel::rowCount(); |
814 | + if (currentCount <= m_limit) { |
815 | + // We are already under the limit so |
816 | + // so just proceed with the removal normally |
817 | + beginRemoveRows(mapFromSource(parent), start, end); |
818 | + m_sourceRemoving = true; |
819 | + } else if (currentCount - nSourceRemovedItems >= m_limit) { |
820 | + // Even after removing items we will be at or over the limit |
821 | + // So to our users we are not removing anything, just changing the data |
822 | + // i.e. we had a internal model with |
823 | + // A B C D E F G H |
824 | + // and a limit of 5, our users just see |
825 | + // A B C D E |
826 | + // so if we remove 3 items starting at 1 we have to expose |
827 | + // A E F G H |
828 | + // that is, a dataChanged from 1 to 4 |
829 | + m_dataChangedBegin = start; |
830 | + m_dataChangedEnd = m_limit - 1; |
831 | + } else { // currentCount > m_limit && currentCount - nSourceRemovedItems < m_limit |
832 | + // We have more items than the limit but after removing we will be below it |
833 | + // So to our users we both removing and changing the data |
834 | + // i.e. we had a internal model with |
835 | + // A B C D E F G |
836 | + // and a limit of 5, our users just see |
837 | + // A B C D E |
838 | + // so if we remove items from 1 to 3 we have to expose |
839 | + // A E F G |
840 | + // that is, a remove from 4 to 4 and a dataChanged from 1 to 3 |
841 | + const int nItemsToRemove = m_limit - (currentCount - nSourceRemovedItems); |
842 | + beginRemoveRows(mapFromSource(parent), m_limit - nItemsToRemove, m_limit - 1); |
843 | + m_sourceRemoving = true; |
844 | + m_dataChangedBegin = start; |
845 | + m_dataChangedEnd = m_limit - nItemsToRemove - 1; |
846 | + if (m_dataChangedBegin > m_dataChangedEnd) { |
847 | + m_dataChangedBegin = -1; |
848 | + m_dataChangedEnd = -1; |
849 | + } |
850 | + } |
851 | + } |
852 | +} |
853 | + |
854 | +void LimitProxyModel::sourceRowsInserted(const QModelIndex & /*parent*/, int /*start*/, int /*end*/) |
855 | +{ |
856 | + if (m_sourceInserting) { |
857 | + endInsertRows(); |
858 | + m_sourceInserting = false; |
859 | + } |
860 | + if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) { |
861 | + dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0)); |
862 | + m_dataChangedBegin = -1; |
863 | + m_dataChangedEnd = -1; |
864 | + } |
865 | +} |
866 | + |
867 | +void LimitProxyModel::sourceRowsRemoved(const QModelIndex & /*parent*/, int /*start*/, int /*end*/) |
868 | +{ |
869 | + if (m_sourceRemoving) { |
870 | + endRemoveRows(); |
871 | + m_sourceRemoving = false; |
872 | + } |
873 | + if (m_dataChangedBegin != -1 && m_dataChangedEnd != -1) { |
874 | + dataChanged(index(m_dataChangedBegin, 0), index(m_dataChangedEnd, 0)); |
875 | + m_dataChangedBegin = -1; |
876 | + m_dataChangedEnd = -1; |
877 | + } |
878 | +} |
879 | |
880 | === added file 'src/app/webbrowser/limit-proxy-model.h' |
881 | --- src/app/webbrowser/limit-proxy-model.h 1970-01-01 00:00:00 +0000 |
882 | +++ src/app/webbrowser/limit-proxy-model.h 2014-07-04 14:19:02 +0000 |
883 | @@ -0,0 +1,70 @@ |
884 | +/* |
885 | + * Copyright 2014 Canonical Ltd. |
886 | + * |
887 | + * This file is part of webbrowser-app. |
888 | + * |
889 | + * webbrowser-app is free software; you can redistribute it and/or modify |
890 | + * it under the terms of the GNU General Public License as published by |
891 | + * the Free Software Foundation; version 3. |
892 | + * |
893 | + * webbrowser-app is distributed in the hope that it will be useful, |
894 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
895 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
896 | + * GNU General Public License for more details. |
897 | + * |
898 | + * You should have received a copy of the GNU General Public License |
899 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
900 | + */ |
901 | + |
902 | +#ifndef __LIMIT_PROXY_MODEL_H__ |
903 | +#define __LIMIT_PROXY_MODEL_H__ |
904 | + |
905 | +// Qt |
906 | +#include <QtCore/QIdentityProxyModel> |
907 | + |
908 | +class QSortFilterProxyModel; |
909 | + |
910 | +class LimitProxyModel : public QIdentityProxyModel |
911 | +{ |
912 | + Q_OBJECT |
913 | + |
914 | + Q_PROPERTY(QObject* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged) |
915 | + Q_PROPERTY(int limit READ limit WRITE setLimit NOTIFY limitChanged) |
916 | + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
917 | + Q_PROPERTY(int unlimitedCount READ unlimitedRowCount NOTIFY unlimitedCountChanged) |
918 | + |
919 | +public: |
920 | + LimitProxyModel(QObject* parent=0); |
921 | + |
922 | + QAbstractItemModel* sourceModel() const; |
923 | + void setSourceModel(QObject* sourceModel); |
924 | + |
925 | + int limit() const; |
926 | + void setLimit(int limit); |
927 | + |
928 | + int rowCount(const QModelIndex &parent = QModelIndex()) const; |
929 | + int unlimitedRowCount(const QModelIndex &parent = QModelIndex()) const; |
930 | + |
931 | +Q_SIGNALS: |
932 | + void sourceModelChanged() const; |
933 | + void limitChanged() const; |
934 | + void unlimitedCountChanged(); |
935 | + void countChanged(); |
936 | + |
937 | +private Q_SLOTS: |
938 | + void sourceRowsAboutToBeInserted(const QModelIndex &parent, int start, int |
939 | +end); |
940 | + void sourceRowsAboutToBeRemoved(const QModelIndex &parent, int start, int |
941 | +end); |
942 | + void sourceRowsInserted(const QModelIndex &parent, int start, int end); |
943 | + void sourceRowsRemoved(const QModelIndex &parent, int start, int end); |
944 | + |
945 | +private: |
946 | + int m_limit; |
947 | + bool m_sourceInserting; |
948 | + bool m_sourceRemoving; |
949 | + int m_dataChangedBegin; |
950 | + int m_dataChangedEnd; |
951 | +}; |
952 | + |
953 | +#endif // __LIMIT_PROXY_MODEL_H__ |
954 | |
955 | === modified file 'src/app/webbrowser/webbrowser-app.cpp' |
956 | --- src/app/webbrowser/webbrowser-app.cpp 2014-06-30 10:40:59 +0000 |
957 | +++ src/app/webbrowser/webbrowser-app.cpp 2014-07-04 14:19:02 +0000 |
958 | @@ -21,8 +21,10 @@ |
959 | #include "history-model.h" |
960 | #include "history-matches-model.h" |
961 | #include "history-timeframe-model.h" |
962 | +#include "history-byvisits-model.h" |
963 | #include "history-domainlist-model.h" |
964 | #include "history-domainlist-chronological-model.h" |
965 | +#include "limit-proxy-model.h" |
966 | #include "searchengine.h" |
967 | #include "settings.h" |
968 | #include "tabs-model.h" |
969 | @@ -75,8 +77,10 @@ |
970 | qmlRegisterType<HistoryModel>(uri, 0, 1, "HistoryModel"); |
971 | qmlRegisterType<HistoryMatchesModel>(uri, 0, 1, "HistoryMatchesModel"); |
972 | qmlRegisterType<HistoryTimeframeModel>(uri, 0, 1, "HistoryTimeframeModel"); |
973 | + qmlRegisterType<HistoryByVisitsModel>(uri, 0 , 1, "HistoryByVisitsModel"); |
974 | qmlRegisterType<HistoryDomainListModel>(uri, 0, 1, "HistoryDomainListModel"); |
975 | qmlRegisterType<HistoryDomainListChronologicalModel>(uri, 0, 1, "HistoryDomainListChronologicalModel"); |
976 | + qmlRegisterType<LimitProxyModel>(uri, 0 , 1, "LimitProxyModel"); |
977 | qmlRegisterType<TabsModel>(uri, 0, 1, "TabsModel"); |
978 | qmlRegisterType<BookmarksModel>(uri, 0, 1, "BookmarksModel"); |
979 | |
980 | |
981 | === modified file 'tests/autopilot/webbrowser_app/emulators/browser.py' |
982 | --- tests/autopilot/webbrowser_app/emulators/browser.py 2014-06-25 20:38:15 +0000 |
983 | +++ tests/autopilot/webbrowser_app/emulators/browser.py 2014-07-04 14:19:02 +0000 |
984 | @@ -116,6 +116,12 @@ |
985 | def get_geolocation_dialog(self): |
986 | return self.wait_select_single("GeolocationPermissionRequest") |
987 | |
988 | + def get_many_new_tab_view(self): |
989 | + return self.select_many("NewTabView") |
990 | + |
991 | + def get_new_tab_view(self): |
992 | + return self.wait_select_single("NewTabView") |
993 | + |
994 | def get_selection(self): |
995 | return self.wait_select_single(Selection) |
996 | |
997 | |
998 | === modified file 'tests/autopilot/webbrowser_app/tests/__init__.py' |
999 | --- tests/autopilot/webbrowser_app/tests/__init__.py 2014-06-11 11:12:14 +0000 |
1000 | +++ tests/autopilot/webbrowser_app/tests/__init__.py 2014-07-04 14:19:02 +0000 |
1001 | @@ -156,8 +156,6 @@ |
1002 | def assert_activity_view_eventually_hidden(self): |
1003 | self.assertThat(self.main_window.get_many_activity_view, |
1004 | Eventually(Equals([]))) |
1005 | - self.assertThat(self.main_window.get_current_webview().visible, |
1006 | - Eventually(Equals(True))) |
1007 | |
1008 | def ensure_activity_view_visible(self): |
1009 | self.ensure_chrome_is_hidden() |
1010 | @@ -169,6 +167,14 @@ |
1011 | ping = urllib.request.urlopen(self.base_url + "/ping") |
1012 | self.assertThat(ping.read(), Equals(b"pong")) |
1013 | |
1014 | + def assert_new_tab_view_eventually_visible(self): |
1015 | + new_tab_view = self.main_window.get_new_tab_view() |
1016 | + self.assertThat(new_tab_view.visible, Eventually(Equals(True))) |
1017 | + |
1018 | + def assert_new_tab_view_eventually_hidden(self): |
1019 | + self.assertThat(self.main_window.get_many_new_tab_view, |
1020 | + Eventually(Equals([]))) |
1021 | + |
1022 | |
1023 | class StartOpenRemotePageTestCaseBase(BrowserTestCaseBase): |
1024 | |
1025 | |
1026 | === modified file 'tests/autopilot/webbrowser_app/tests/test_tabs.py' |
1027 | --- tests/autopilot/webbrowser_app/tests/test_tabs.py 2014-06-27 11:51:42 +0000 |
1028 | +++ tests/autopilot/webbrowser_app/tests/test_tabs.py 2014-07-04 14:19:02 +0000 |
1029 | @@ -77,6 +77,7 @@ |
1030 | self.assertThat(self.main_window.currentIndex, Equals(0)) |
1031 | self.open_new_tab() |
1032 | self.assertThat(self.main_window.currentIndex, Eventually(Equals(1))) |
1033 | + self.assert_new_tab_view_eventually_visible() |
1034 | |
1035 | def test_switch_tabs(self): |
1036 | self.ensure_activity_view_visible() |
1037 | @@ -167,3 +168,12 @@ |
1038 | tabs = self.main_window.get_tabslist_view_delegates() |
1039 | self.pointing_device.click_object(tabs[0]) |
1040 | self.assertThat(error.visible, Eventually(Equals(False))) |
1041 | + |
1042 | + def test_new_tab_view(self): |
1043 | + self.ensure_activity_view_visible() |
1044 | + self.open_new_tab() |
1045 | + self.assert_new_tab_view_eventually_visible() |
1046 | + url = self.base_url + "/aleaiactaest" |
1047 | + self.type_in_address_bar(url) |
1048 | + self.keyboard.press_and_release("Enter") |
1049 | + self.assert_new_tab_view_eventually_hidden() |
1050 | |
1051 | === modified file 'tests/unittests/CMakeLists.txt' |
1052 | --- tests/unittests/CMakeLists.txt 2014-05-22 15:27:38 +0000 |
1053 | +++ tests/unittests/CMakeLists.txt 2014-07-04 14:19:02 +0000 |
1054 | @@ -6,8 +6,10 @@ |
1055 | add_subdirectory(history-domain-model) |
1056 | add_subdirectory(history-domainlist-model) |
1057 | add_subdirectory(history-domainlist-chronological-model) |
1058 | +add_subdirectory(history-byvisits-model) |
1059 | add_subdirectory(session-utils) |
1060 | add_subdirectory(tabs-model) |
1061 | add_subdirectory(bookmarks-model) |
1062 | +add_subdirectory(limit-proxy-model) |
1063 | add_subdirectory(container-url-patterns) |
1064 | add_subdirectory(cookie-store) |
1065 | |
1066 | === added directory 'tests/unittests/history-byvisits-model' |
1067 | === added file 'tests/unittests/history-byvisits-model/CMakeLists.txt' |
1068 | --- tests/unittests/history-byvisits-model/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1069 | +++ tests/unittests/history-byvisits-model/CMakeLists.txt 2014-07-04 14:19:02 +0000 |
1070 | @@ -0,0 +1,6 @@ |
1071 | +set(TEST tst_HistoryByVisitsModelTests) |
1072 | +add_executable(${TEST} tst_HistoryByVisitsModelTests.cpp) |
1073 | +include_directories(${webbrowser-app_SOURCE_DIR}) |
1074 | +qt5_use_modules(${TEST} Test) |
1075 | +target_link_libraries(${TEST} webbrowser-app-models) |
1076 | +add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml) |
1077 | |
1078 | === added file 'tests/unittests/history-byvisits-model/tst_HistoryByVisitsModelTests.cpp' |
1079 | --- tests/unittests/history-byvisits-model/tst_HistoryByVisitsModelTests.cpp 1970-01-01 00:00:00 +0000 |
1080 | +++ tests/unittests/history-byvisits-model/tst_HistoryByVisitsModelTests.cpp 2014-07-04 14:19:02 +0000 |
1081 | @@ -0,0 +1,89 @@ |
1082 | +/* |
1083 | + * Copyright 2014 Canonical Ltd. |
1084 | + * |
1085 | + * This file is part of webbrowser-app. |
1086 | + * |
1087 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1088 | + * it under the terms of the GNU General Public License as published by |
1089 | + * the Free Software Foundation; version 3. |
1090 | + * |
1091 | + * webbrowser-app is distributed in the hope that it will be useful, |
1092 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1093 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1094 | + * GNU General Public License for more details. |
1095 | + * |
1096 | + * You should have received a copy of the GNU General Public License |
1097 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1098 | + */ |
1099 | + |
1100 | +// Qt |
1101 | +#include <QtTest/QSignalSpy> |
1102 | +#include <QtTest/QtTest> |
1103 | + |
1104 | +// local |
1105 | +#include "domain-utils.h" |
1106 | +#include "history-model.h" |
1107 | +#include "history-timeframe-model.h" |
1108 | +#include "history-byvisits-model.h" |
1109 | + |
1110 | +class HistoryByVisitsModelTests : public QObject |
1111 | +{ |
1112 | + Q_OBJECT |
1113 | + |
1114 | +private: |
1115 | + HistoryModel* history; |
1116 | + HistoryTimeframeModel* timeframe; |
1117 | + HistoryByVisitsModel* model; |
1118 | + |
1119 | +private Q_SLOTS: |
1120 | + void init() |
1121 | + { |
1122 | + history = new HistoryModel; |
1123 | + history->setDatabasePath(":memory:"); |
1124 | + timeframe = new HistoryTimeframeModel; |
1125 | + timeframe->setSourceModel(history); |
1126 | + model = new HistoryByVisitsModel; |
1127 | + model->setSourceModel(timeframe); |
1128 | + } |
1129 | + |
1130 | + void cleanup() |
1131 | + { |
1132 | + delete model; |
1133 | + delete timeframe; |
1134 | + delete history; |
1135 | + } |
1136 | + |
1137 | + void shouldBeInitiallyEmpty() |
1138 | + { |
1139 | + QCOMPARE(model->rowCount(), 0); |
1140 | + } |
1141 | + |
1142 | + void shouldNotifyWhenChangingSourceModel() |
1143 | + { |
1144 | + QSignalSpy spy(model, SIGNAL(sourceModelChanged())); |
1145 | + model->setSourceModel(timeframe); |
1146 | + QVERIFY(spy.isEmpty()); |
1147 | + HistoryTimeframeModel* timeframe2 = new HistoryTimeframeModel; |
1148 | + model->setSourceModel(timeframe2); |
1149 | + QCOMPARE(spy.count(), 1); |
1150 | + QCOMPARE(model->sourceModel(), timeframe2); |
1151 | + model->setSourceModel(0); |
1152 | + QCOMPARE(spy.count(), 2); |
1153 | + QCOMPARE(model->sourceModel(), (HistoryTimeframeModel*) 0); |
1154 | + delete timeframe2; |
1155 | + } |
1156 | + |
1157 | + void shouldBeSortedByVisits() |
1158 | + { |
1159 | + history->add(QUrl("http://example.org/"), "Example Domain", QUrl()); |
1160 | + QTest::qWait(1001); |
1161 | + history->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl()); |
1162 | + QTest::qWait(1001); |
1163 | + history->add(QUrl("http://ubuntu.com/"), "Ubuntu", QUrl()); |
1164 | + QCOMPARE(model->data(model->index(0, 0), HistoryModel::Domain).toString(), QString("ubuntu.com")); |
1165 | + QCOMPARE(model->data(model->index(1, 0), HistoryModel::Domain).toString(), QString("example.org")); |
1166 | + } |
1167 | +}; |
1168 | + |
1169 | +QTEST_MAIN(HistoryByVisitsModelTests) |
1170 | +#include "tst_HistoryByVisitsModelTests.moc" |
1171 | |
1172 | === added directory 'tests/unittests/limit-proxy-model' |
1173 | === added file 'tests/unittests/limit-proxy-model/CMakeLists.txt' |
1174 | --- tests/unittests/limit-proxy-model/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1175 | +++ tests/unittests/limit-proxy-model/CMakeLists.txt 2014-07-04 14:19:02 +0000 |
1176 | @@ -0,0 +1,6 @@ |
1177 | +set(TEST tst_LimitProxyModelTests) |
1178 | +add_executable(${TEST} tst_LimitProxyModelTests.cpp) |
1179 | +include_directories(${webbrowser-app_SOURCE_DIR}) |
1180 | +qt5_use_modules(${TEST} Test) |
1181 | +target_link_libraries(${TEST} webbrowser-app-models) |
1182 | +add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml) |
1183 | |
1184 | === added file 'tests/unittests/limit-proxy-model/tst_LimitProxyModelTests.cpp' |
1185 | --- tests/unittests/limit-proxy-model/tst_LimitProxyModelTests.cpp 1970-01-01 00:00:00 +0000 |
1186 | +++ tests/unittests/limit-proxy-model/tst_LimitProxyModelTests.cpp 2014-07-04 14:19:02 +0000 |
1187 | @@ -0,0 +1,144 @@ |
1188 | +/* |
1189 | + * Copyright 2014 Canonical Ltd. |
1190 | + * |
1191 | + * This file is part of webbrowser-app. |
1192 | + * |
1193 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1194 | + * it under the terms of the GNU General Public License as published by |
1195 | + * the Free Software Foundation; version 3. |
1196 | + * |
1197 | + * webbrowser-app is distributed in the hope that it will be useful, |
1198 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1199 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1200 | + * GNU General Public License for more details. |
1201 | + * |
1202 | + * You should have received a copy of the GNU General Public License |
1203 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1204 | + */ |
1205 | + |
1206 | +// Qt |
1207 | +#include <QtTest/QSignalSpy> |
1208 | +#include <QtTest/QtTest> |
1209 | + |
1210 | +// local |
1211 | +#include "domain-utils.h" |
1212 | +#include "history-model.h" |
1213 | +#include "history-timeframe-model.h" |
1214 | +#include "history-byvisits-model.h" |
1215 | +#include "limit-proxy-model.h" |
1216 | + |
1217 | +class LimitProxyModelTests : public QObject |
1218 | +{ |
1219 | + Q_OBJECT |
1220 | + |
1221 | +private: |
1222 | + HistoryModel* history; |
1223 | + HistoryTimeframeModel* timeframe; |
1224 | + HistoryByVisitsModel* byvisits; |
1225 | + LimitProxyModel* model; |
1226 | + |
1227 | +private Q_SLOTS: |
1228 | + void init() |
1229 | + { |
1230 | + history = new HistoryModel; |
1231 | + history->setDatabasePath(":memory:"); |
1232 | + timeframe = new HistoryTimeframeModel; |
1233 | + timeframe->setSourceModel(history); |
1234 | + byvisits = new HistoryByVisitsModel; |
1235 | + byvisits->setSourceModel(timeframe); |
1236 | + model = new LimitProxyModel; |
1237 | + model->setSourceModel(byvisits); |
1238 | + } |
1239 | + |
1240 | + void cleanup() |
1241 | + { |
1242 | + delete model; |
1243 | + delete byvisits; |
1244 | + delete timeframe; |
1245 | + delete history; |
1246 | + } |
1247 | + |
1248 | + void shouldBeInitiallyEmpty() |
1249 | + { |
1250 | + QCOMPARE(model->rowCount(), 0); |
1251 | + } |
1252 | + |
1253 | + void shouldLimitBeInitiallyMinusOne() |
1254 | + { |
1255 | + QCOMPARE(model->limit(), -1); |
1256 | + } |
1257 | + |
1258 | + void shouldNotifyWhenChangingSourceModel() |
1259 | + { |
1260 | + QSignalSpy spy(model, SIGNAL(sourceModelChanged())); |
1261 | + model->setSourceModel(byvisits); |
1262 | + QVERIFY(spy.isEmpty()); |
1263 | + HistoryByVisitsModel* byvisits2 = new HistoryByVisitsModel; |
1264 | + model->setSourceModel(byvisits2); |
1265 | + QCOMPARE(spy.count(), 1); |
1266 | + QCOMPARE(model->sourceModel(), byvisits2); |
1267 | + model->setSourceModel(0); |
1268 | + QCOMPARE(spy.count(), 2); |
1269 | + QCOMPARE(model->sourceModel(), (HistoryByVisitsModel*) 0); |
1270 | + delete byvisits2; |
1271 | + } |
1272 | + |
1273 | + void shouldLimitEntriesWithLimitSetBeforePopulating() |
1274 | + { |
1275 | + model->setLimit(2); |
1276 | + |
1277 | + history->add(QUrl("http://example1.org/"), "Example 1 Domain", QUrl()); |
1278 | + QTest::qWait(1001); |
1279 | + history->add(QUrl("http://example2.org/"), "Example 2 Domain", QUrl()); |
1280 | + QTest::qWait(1001); |
1281 | + history->add(QUrl("http://example3.org/"), "Example 3 Domain", QUrl()); |
1282 | + |
1283 | + QCOMPARE(model->rowCount(), 2); |
1284 | + QCOMPARE(model->unlimitedRowCount(), 3); |
1285 | + } |
1286 | + |
1287 | + void shouldLimitEntriesWithLimitSetAfterPopulating() |
1288 | + { |
1289 | + history->add(QUrl("http://example1.org/"), "Example 1 Domain", QUrl()); |
1290 | + QTest::qWait(1001); |
1291 | + history->add(QUrl("http://example2.org/"), "Example 2 Domain", QUrl()); |
1292 | + QTest::qWait(1001); |
1293 | + history->add(QUrl("http://example3.org/"), "Example 3 Domain", QUrl()); |
1294 | + |
1295 | + model->setLimit(2); |
1296 | + |
1297 | + QCOMPARE(model->rowCount(), 2); |
1298 | + QCOMPARE(model->unlimitedRowCount(), 3); |
1299 | + } |
1300 | + |
1301 | + void shouldNotLimitEntriesIfLimitIsMinusOne() |
1302 | + { |
1303 | + model->setLimit(-1); |
1304 | + |
1305 | + history->add(QUrl("http://example1.org/"), "Example 1 Domain", QUrl()); |
1306 | + QTest::qWait(1001); |
1307 | + history->add(QUrl("http://example2.org/"), "Example 2 Domain", QUrl()); |
1308 | + QTest::qWait(1001); |
1309 | + history->add(QUrl("http://example3.org/"), "Example 3 Domain", QUrl()); |
1310 | + |
1311 | + QCOMPARE(model->unlimitedRowCount(), 3); |
1312 | + QCOMPARE(model->rowCount(), model->unlimitedRowCount()); |
1313 | + } |
1314 | + |
1315 | + void shouldNotLimitEntriesIfLimitIsGreaterThanRowCount() |
1316 | + { |
1317 | + model->setLimit(4); |
1318 | + |
1319 | + history->add(QUrl("http://example1.org/"), "Example 1 Domain", QUrl()); |
1320 | + QTest::qWait(1001); |
1321 | + history->add(QUrl("http://example2.org/"), "Example 2 Domain", QUrl()); |
1322 | + QTest::qWait(1001); |
1323 | + history->add(QUrl("http://example3.org/"), "Example 3 Domain", QUrl()); |
1324 | + |
1325 | + QCOMPARE(model->unlimitedRowCount(), 3); |
1326 | + QCOMPARE(model->rowCount(), model->unlimitedRowCount()); |
1327 | + } |
1328 | +}; |
1329 | + |
1330 | +QTEST_MAIN(LimitProxyModelTests) |
1331 | +#include "tst_LimitProxyModelTests.moc" |
PASSED: Continuous integration, rev:563 jenkins. qa.ubuntu. com/job/ webbrowser- app-ci/ 893/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- utopic- touch/1246 jenkins. qa.ubuntu. com/job/ generic- mediumtests- utopic/ 1102 jenkins. qa.ubuntu. com/job/ webbrowser- app-utopic- amd64-ci/ 92 jenkins. qa.ubuntu. com/job/ webbrowser- app-utopic- armhf-ci/ 92 jenkins. qa.ubuntu. com/job/ webbrowser- app-utopic- armhf-ci/ 92/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ webbrowser- app-utopic- i386-ci/ 92 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- mako/1570 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/2131 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/2131/ artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 8889 jenkins. qa.ubuntu. com/job/ autopilot- testrunner- otto-utopic/ 908 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/1245 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- amd64/1245/ artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/webbrowser- app-ci/ 893/rebuild
http://