Merge lp:~osomon/webbrowser-app/keyboard-nav-narrow into lp:webbrowser-app

Proposed by Olivier Tilloy
Status: Merged
Approved by: Olivier Tilloy
Approved revision: 1363
Merged at revision: 1350
Proposed branch: lp:~osomon/webbrowser-app/keyboard-nav-narrow
Merge into: lp:webbrowser-app
Prerequisite: lp:~osomon/webbrowser-app/bottomedgebar-autopilot-tests
Diff against target: 2400 lines (+1100/-505)
25 files modified
src/app/webbrowser/BookmarksFoldersView.qml (+212/-125)
src/app/webbrowser/BookmarksFoldersViewWide.qml (+1/-6)
src/app/webbrowser/BookmarksView.qml (+2/-3)
src/app/webbrowser/Browser.qml (+33/-29)
src/app/webbrowser/BrowserPageHeader.qml (+2/-1)
src/app/webbrowser/DownloadDelegate.qml (+1/-0)
src/app/webbrowser/DownloadsPage.qml (+21/-4)
src/app/webbrowser/ExpandedHistoryView.qml (+16/-6)
src/app/webbrowser/HistoryView.qml (+13/-3)
src/app/webbrowser/ListViewHighlight.qml (+11/-2)
src/app/webbrowser/NewTabView.qml (+245/-130)
src/app/webbrowser/NewTabViewWide.qml (+1/-11)
src/app/webbrowser/ToolbarAction.qml (+1/-0)
src/app/webbrowser/UrlDelegate.qml (+16/-5)
src/app/webbrowser/UrlPreviewDelegate.qml (+5/-1)
src/app/webbrowser/UrlPreviewGrid.qml (+23/-16)
src/app/webbrowser/UrlsList.qml (+0/-52)
tests/autopilot/webbrowser_app/emulators/browser.py (+9/-17)
tests/autopilot/webbrowser_app/tests/test_new_tab_view.py (+13/-18)
tests/unittests/qml/CMakeLists.txt (+2/-0)
tests/unittests/qml/tst_BookmarksView.qml (+82/-3)
tests/unittests/qml/tst_HistoryView.qml (+50/-4)
tests/unittests/qml/tst_NewTabView.qml (+292/-0)
tests/unittests/qml/tst_NewTabViewWide.qml (+1/-10)
tests/unittests/qml/tst_QmlTests.cpp (+48/-59)
To merge this branch: bzr merge lp:~osomon/webbrowser-app/keyboard-nav-narrow
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+285098@code.launchpad.net

Commit message

Add keyboard navigation support to narrow views (new tab view, history view, bookmarks view, downloads view).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1360. By Olivier Tilloy

Ensure the state of the NewTabView under test is reset between each test run.

1361. By Olivier Tilloy

Clip expanded history view contents so list items are not drawn over the header.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1362. By Olivier Tilloy

Show the keyboard highlight only when there’s actually a physical keyboard connected.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1363. By Olivier Tilloy

Fix unit tests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/app/webbrowser/BookmarksFoldersView.qml'
2--- src/app/webbrowser/BookmarksFoldersView.qml 2015-11-12 16:16:15 +0000
3+++ src/app/webbrowser/BookmarksFoldersView.qml 2016-02-11 08:27:04 +0000
4@@ -1,5 +1,5 @@
5 /*
6- * Copyright 2015 Canonical Ltd.
7+ * Copyright 2015-2016 Canonical Ltd.
8 *
9 * This file is part of webbrowser-app.
10 *
11@@ -28,6 +28,8 @@
12 property alias interactive: bookmarksFolderListView.interactive
13 property url homeBookmarkUrl
14
15+ readonly property Item currentItem: bookmarksFolderListView.currentItem ? bookmarksFolderListView.currentItem.currentItem : null
16+
17 signal bookmarkClicked(url url)
18 signal bookmarkRemoved(url url)
19
20@@ -54,10 +56,28 @@
21 }
22
23 height: active ? item.height : 0
24- active: entries.count > 0
25-
26- sourceComponent: Item {
27+ active: (entries.count > 0) || !folder
28+
29+ readonly property Item currentItem: active ? item.currentItem : null
30+
31+ onActiveChanged: {
32+ if (!active && activeFocus) {
33+ bookmarksFolderListView.decrementCurrentIndex()
34+ }
35+ }
36+
37+ sourceComponent: FocusScope {
38 objectName: "bookmarkFolderDelegate"
39+ focus: true
40+
41+ readonly property Item currentItem: activeFocus ? (bookmarkFolderHeader.focus ? bookmarkFolderHeader : bookmarksInFolderLoader.item.currentItem) : null
42+
43+ function focusHeader() {
44+ bookmarkFolderHeader.focus = true
45+ }
46+ function focusBookmarks() {
47+ bookmarksInFolderLoader.focus = true
48+ }
49
50 property string folderName: folder
51
52@@ -66,127 +86,194 @@
53 right: parent ? parent.right : undefined
54 }
55
56- height: delegateColumn.height
57-
58- Column {
59- id: delegateColumn
60-
61- property bool expanded: folderName ? false : true
62-
63- anchors {
64- left: parent.left
65- right: parent.right
66- }
67-
68- Item {
69- objectName: "bookmarkFolderHeader"
70-
71- anchors {
72- left: parent.left
73- right: parent.right
74- leftMargin: units.gu(2)
75- rightMargin: units.gu(2)
76- }
77-
78- height: units.gu(6.5)
79-
80- Row {
81- anchors {
82- left: parent.left
83- leftMargin: units.gu(1.5)
84- right: parent.right
85- }
86-
87- height: units.gu(6)
88- spacing: units.gu(1.5)
89-
90- Icon {
91- id: expandedIcon
92- name: delegateColumn.expanded ? "go-down" : "go-next"
93-
94- height: units.gu(2)
95- width: height
96-
97- anchors {
98- leftMargin: units.gu(1)
99- topMargin: units.gu(2)
100- top: parent.top
101- }
102- }
103-
104- Label {
105- width: parent.width - expandedIcon.width - units.gu(3)
106- anchors.verticalCenter: expandedIcon.verticalCenter
107-
108- text: folderName ? folderName : i18n.tr("All Bookmarks")
109- fontSize: "small"
110- }
111- }
112-
113- ListItem.ThinDivider {
114- anchors {
115- left: parent.left
116- right: parent.right
117- bottom: parent.bottom
118- bottomMargin: units.gu(1)
119- }
120- }
121-
122- MouseArea {
123- anchors.fill: parent
124- onClicked: delegateColumn.expanded = !delegateColumn.expanded
125- }
126- }
127-
128- Loader {
129- anchors {
130- left: parent.left
131- right: parent.right
132- }
133-
134- height: item ? item.contentHeight : 0
135-
136- visible: status == Loader.Ready
137-
138- active: delegateColumn.expanded
139- sourceComponent: ListView {
140- readonly property bool isAllBookmarksFolder: folder === ""
141-
142- interactive: false
143-
144- model: {
145- if (isAllBookmarksFolder) {
146- return BookmarksModelUtils.prependHomepageToBookmarks(entries, {
147- title: i18n.tr("Homepage"),
148- url: bookmarksFoldersViewItem.homeBookmarkUrl
149- })
150- }
151-
152- return entries
153- }
154-
155- delegate: UrlDelegate{
156- id: urlDelegate
157- objectName: "urlDelegate_" + index
158-
159- property var entry: isAllBookmarksFolder ? modelData : model
160-
161- width: parent.width
162- height: units.gu(5)
163-
164- removable: !isAllBookmarksFolder || index !== 0
165-
166- icon: entry.icon ? entry.icon : ""
167- title: entry.title ? entry.title : entry.url
168- url: entry.url
169-
170- onClicked: bookmarksFoldersViewItem.bookmarkClicked(url)
171- onRemoved: bookmarksFoldersViewItem.bookmarkRemoved(url)
172- }
173- }
174- }
175- }
176- }
177+ height: childrenRect.height
178+
179+ property bool expanded: folderName ? false : true
180+
181+ Item {
182+ id: bookmarkFolderHeader
183+ objectName: "bookmarkFolderHeader"
184+
185+ anchors {
186+ top: parent.top
187+ left: parent.left
188+ right: parent.right
189+ }
190+ height: units.gu(6.5)
191+ focus: true
192+
193+ Row {
194+ anchors {
195+ left: parent.left
196+ leftMargin: units.gu(2.5)
197+ right: parent.right
198+ }
199+
200+ height: units.gu(6)
201+ spacing: units.gu(1.5)
202+
203+ Icon {
204+ id: expandedIcon
205+ name: expanded ? "go-down" : "go-next"
206+
207+ height: units.gu(2)
208+ width: height
209+
210+ anchors {
211+ leftMargin: units.gu(1)
212+ topMargin: units.gu(2)
213+ top: parent.top
214+ }
215+ }
216+
217+ Label {
218+ width: parent.width - expandedIcon.width - units.gu(3)
219+ anchors.verticalCenter: expandedIcon.verticalCenter
220+
221+ text: folderName ? folderName : i18n.tr("All Bookmarks")
222+ fontSize: "small"
223+ }
224+ }
225+
226+ ListItem.ThinDivider {
227+ anchors {
228+ left: parent.left
229+ right: parent.right
230+ bottom: parent.bottom
231+ bottomMargin: units.gu(1)
232+ }
233+ }
234+
235+ ListViewHighlight {
236+ anchors.fill: parent
237+ visible: hasKeyboard && parent.activeFocus
238+ }
239+
240+ MouseArea {
241+ anchors.fill: parent
242+ onClicked: expanded = !expanded
243+ }
244+
245+ Keys.onEnterPressed: expanded = !expanded
246+ Keys.onReturnPressed: expanded = !expanded
247+ Keys.onSpacePressed: expanded = !expanded
248+ }
249+
250+ Loader {
251+ id: bookmarksInFolderLoader
252+ anchors {
253+ top: bookmarkFolderHeader.bottom
254+ left: parent.left
255+ right: parent.right
256+ }
257+
258+ height: item ? item.contentHeight : 0
259+
260+ visible: status == Loader.Ready
261+
262+ active: expanded
263+ onActiveChanged: {
264+ if (!active && focus) {
265+ focusHeader()
266+ }
267+ }
268+
269+ sourceComponent: ListView {
270+ readonly property bool isAllBookmarksFolder: folder === ""
271+
272+ focus: true
273+ interactive: false
274+ currentIndex: 0
275+
276+ model: {
277+ if (isAllBookmarksFolder) {
278+ return BookmarksModelUtils.prependHomepageToBookmarks(entries, {
279+ title: i18n.tr("Homepage"),
280+ url: bookmarksFoldersViewItem.homeBookmarkUrl
281+ })
282+ }
283+
284+ return entries
285+ }
286+
287+ delegate: UrlDelegate{
288+ id: urlDelegate
289+ objectName: "urlDelegate_%1".arg(index)
290+
291+ property var entry: isAllBookmarksFolder ? modelData : model
292+
293+ width: parent.width
294+ height: units.gu(5)
295+
296+ removable: !isAllBookmarksFolder || index !== 0
297+
298+ icon: entry.icon ? entry.icon : ""
299+ title: entry.title ? entry.title : entry.url
300+ url: entry.url
301+
302+ onClicked: bookmarksFoldersViewItem.bookmarkClicked(url)
303+ onRemoved: bookmarksFoldersViewItem.bookmarkRemoved(url)
304+ }
305+
306+ highlight: ListViewHighlight {}
307+
308+ Keys.onUpPressed: {
309+ if (currentIndex > 0) {
310+ --currentIndex
311+ } else {
312+ focusHeader()
313+ }
314+ }
315+ Keys.onDownPressed: {
316+ if (currentIndex < (count - 1)) {
317+ ++currentIndex
318+ } else {
319+ event.accepted = false
320+ }
321+ }
322+ Keys.onEnterPressed: currentItem.clicked()
323+ Keys.onReturnPressed: currentItem.clicked()
324+ Keys.onDeletePressed: currentItem.removed()
325+ }
326+ }
327+
328+ Keys.onDownPressed: {
329+ if (expanded && !bookmarksInFolderLoader.focus) {
330+ focusBookmarks()
331+ } else {
332+ event.accepted = false
333+ }
334+ }
335+ }
336+ }
337+
338+ Keys.onUpPressed: {
339+ var current = currentIndex
340+ --currentIndex
341+ while (currentItem && !currentItem.active) {
342+ --currentIndex
343+ }
344+ if (!currentItem) {
345+ currentIndex = current
346+ event.accepted = false
347+ }
348+ }
349+ Keys.onDownPressed: {
350+ var current = currentIndex
351+ ++currentIndex
352+ while (currentItem && !currentItem.active) {
353+ ++currentIndex
354+ }
355+ if (!currentItem || !currentItem.active) {
356+ currentIndex = current
357+ }
358+ }
359+ }
360+
361+ // Initially focus the first bookmark
362+ Component.onCompleted: {
363+ if (bookmarksFolderListView.currentItem) {
364+ bookmarksFolderListView.currentItem.item.focusBookmarks()
365 }
366 }
367 }
368
369=== modified file 'src/app/webbrowser/BookmarksFoldersViewWide.qml'
370--- src/app/webbrowser/BookmarksFoldersViewWide.qml 2016-01-28 18:35:14 +0000
371+++ src/app/webbrowser/BookmarksFoldersViewWide.qml 2016-02-11 08:27:04 +0000
372@@ -33,7 +33,7 @@
373 function restoreLastFocusedColumn() {
374 if (internal.lastFocusedColumn &&
375 internal.lastFocusedColumn == bookmarksList &&
376- model.count > 0) {
377+ BookmarksModel.count > 0) {
378 bookmarksList.forceActiveFocus()
379 } else {
380 folders.forceActiveFocus()
381@@ -46,11 +46,6 @@
382 }
383 }
384
385- Rectangle {
386- anchors.fill: parent
387- color: "#fbfbfb"
388- }
389-
390 ListView {
391 id: folders
392 objectName: "foldersList"
393
394=== modified file 'src/app/webbrowser/BookmarksView.qml'
395--- src/app/webbrowser/BookmarksView.qml 2015-12-17 20:25:21 +0000
396+++ src/app/webbrowser/BookmarksView.qml 2016-02-11 08:27:04 +0000
397@@ -1,5 +1,5 @@
398 /*
399- * Copyright 2015 Canonical Ltd.
400+ * Copyright 2015-2016 Canonical Ltd.
401 *
402 * This file is part of webbrowser-app.
403 *
404@@ -44,7 +44,6 @@
405 left: parent.left
406 right: parent.right
407 bottom: toolbar.top
408- rightMargin: units.gu(2)
409 }
410
411 interactive: true
412@@ -109,8 +108,8 @@
413 verticalCenter: parent.verticalCenter
414 }
415
416+ activeFocusOnPress: false
417 strokeColor: UbuntuColors.darkGrey
418-
419 text: i18n.tr("Done")
420
421 onClicked: bookmarksView.done()
422
423=== modified file 'src/app/webbrowser/Browser.qml'
424--- src/app/webbrowser/Browser.qml 2016-02-11 08:27:04 +0000
425+++ src/app/webbrowser/Browser.qml 2016-02-11 08:27:04 +0000
426@@ -290,6 +290,7 @@
427 currentWebview.url = url
428 tabContainer.forceActiveFocus()
429 }
430+ Keys.onUpPressed: chrome.focus = true
431 }
432 }
433
434@@ -311,7 +312,7 @@
435 currentWebview.url = url
436 tabContainer.forceActiveFocus()
437 }
438- onReleasingKeyboardFocus: chrome.focus = true
439+ Keys.onUpPressed: chrome.focus = true
440 }
441 }
442
443@@ -873,40 +874,43 @@
444 Component {
445 id: historyViewComponent
446
447- HistoryView {
448- anchors.fill: parent
449+ FocusScope {
450+ signal loadModel()
451+ onLoadModel: children[0].loadModel()
452
453- onSeeMoreEntriesClicked: {
454- var view = expandedHistoryViewComponent.createObject(expandedHistoryViewContainer, {model: model})
455- view.onHistoryEntryClicked.connect(done)
456+ HistoryView {
457+ anchors.fill: parent
458+ focus: !expandedHistoryViewLoader.focus
459+ visible: focus
460+ onSeeMoreEntriesClicked: {
461+ expandedHistoryViewLoader.model = model
462+ expandedHistoryViewLoader.active = true
463+ }
464+ onNewTabRequested: browser.openUrlInNewTab("", true)
465+ onDone: historyViewLoader.active = false
466 }
467- onNewTabRequested: browser.openUrlInNewTab("", true)
468- onDone: historyViewLoader.active = false
469-
470- FocusScope {
471- id: expandedHistoryViewContainer
472-
473- visible: children.length > 0
474+
475+ Loader {
476+ id: expandedHistoryViewLoader
477+ asynchronous: true
478 anchors.fill: parent
479-
480- Component {
481- id: expandedHistoryViewComponent
482-
483- ExpandedHistoryView {
484- anchors.fill: parent
485-
486- onHistoryEntryClicked: {
487- browser.openUrlInNewTab(url, true)
488+ active: false
489+ focus: active
490+ property var model: null
491+ sourceComponent: ExpandedHistoryView {
492+ focus: true
493+ model: expandedHistoryViewLoader.model
494+ onHistoryEntryClicked: {
495+ browser.openUrlInNewTab(url, true)
496+ historyViewLoader.active = false
497+ }
498+ onHistoryEntryRemoved: {
499+ if (count == 1) {
500 done()
501 }
502- onHistoryEntryRemoved: {
503- if (count == 1) {
504- done()
505- }
506- HistoryModel.removeEntryByUrl(url)
507- }
508- onDone: destroy()
509+ HistoryModel.removeEntryByUrl(url)
510 }
511+ onDone: expandedHistoryViewLoader.active = false
512 }
513 }
514 }
515
516=== modified file 'src/app/webbrowser/BrowserPageHeader.qml'
517--- src/app/webbrowser/BrowserPageHeader.qml 2016-01-12 17:41:47 +0000
518+++ src/app/webbrowser/BrowserPageHeader.qml 2016-02-11 08:27:04 +0000
519@@ -1,5 +1,5 @@
520 /*
521- * Copyright 2015 Canonical Ltd.
522+ * Copyright 2015-2016 Canonical Ltd.
523 *
524 * This file is part of webbrowser-app.
525 *
526@@ -62,6 +62,7 @@
527
528 width: height
529
530+ activeFocusOnPress: false
531 onTriggered: root.back()
532 anchors {
533 top: parent.top
534
535=== modified file 'src/app/webbrowser/DownloadDelegate.qml'
536--- src/app/webbrowser/DownloadDelegate.qml 2016-01-12 11:40:57 +0000
537+++ src/app/webbrowser/DownloadDelegate.qml 2016-02-11 08:27:04 +0000
538@@ -67,6 +67,7 @@
539 left: parent.left
540 leftMargin: units.gu(2)
541 right: parent.right
542+ rightMargin: units.gu(2)
543 }
544
545 Item {
546
547=== modified file 'src/app/webbrowser/DownloadsPage.qml'
548--- src/app/webbrowser/DownloadsPage.qml 2016-01-28 18:40:31 +0000
549+++ src/app/webbrowser/DownloadsPage.qml 2016-02-11 08:27:04 +0000
550@@ -148,7 +148,7 @@
551 ListView {
552 id: downloadsListView
553 clip: true
554- focus: true
555+ focus: !exportPeerPicker.focus
556
557 anchors {
558 fill: parent
559@@ -223,6 +223,23 @@
560
561 Keys.onEnterPressed: currentItem.clicked()
562 Keys.onReturnPressed: currentItem.clicked()
563+ Keys.onEscapePressed: {
564+ if (selectMode) {
565+ selectMode = false
566+ } else {
567+ event.accepted = false
568+ }
569+ }
570+ Keys.onSpacePressed: {
571+ if (selectMode || pickingMode) {
572+ currentItem.clicked()
573+ }
574+ }
575+ Keys.onDeletePressed: {
576+ if (!selectMode && !pickingMode) {
577+ currentItem.removed()
578+ }
579+ }
580 }
581
582 Label {
583@@ -243,6 +260,7 @@
584 ContentPeerPicker {
585 id: exportPeerPicker
586 visible: false
587+ focus: visible
588 anchors.fill: parent
589 handler: ContentHandler.Destination
590 property string path
591@@ -254,9 +272,8 @@
592 }
593 visible = false
594 }
595- onCancelPressed: {
596- visible = false
597- }
598+ onCancelPressed: visible = false
599+ Keys.onEscapePressed: visible = false
600 }
601
602 }
603
604=== modified file 'src/app/webbrowser/ExpandedHistoryView.qml'
605--- src/app/webbrowser/ExpandedHistoryView.qml 2015-11-23 10:35:05 +0000
606+++ src/app/webbrowser/ExpandedHistoryView.qml 2016-02-11 08:27:04 +0000
607@@ -1,5 +1,5 @@
608 /*
609- * Copyright 2014-2015 Canonical Ltd.
610+ * Copyright 2014-2016 Canonical Ltd.
611 *
612 * This file is part of webbrowser-app.
613 *
614@@ -20,7 +20,7 @@
615 import Ubuntu.Components 1.3
616 import ".."
617
618-Item {
619+FocusScope {
620 id: expandedHistoryView
621
622 property alias model: entriesListView.model
623@@ -44,13 +44,16 @@
624 ListView {
625 id: entriesListView
626
627+ focus: true
628+ clip: true
629+
630 anchors {
631 top: header.bottom
632+ topMargin: units.gu(1.5)
633 bottom: parent.bottom
634+ bottomMargin: units.gu(1.5)
635 left: parent.left
636 right: parent.right
637- margins: units.gu(1.5)
638- leftMargin: 0
639 }
640
641 section.property: "lastVisitDate"
642@@ -75,6 +78,13 @@
643 onClicked: expandedHistoryView.historyEntryClicked(model.url)
644 onRemoved: expandedHistoryView.historyEntryRemoved(model.url)
645 }
646+
647+ highlight: ListViewHighlight {}
648+
649+ Keys.onEnterPressed: currentItem.clicked()
650+ Keys.onReturnPressed: currentItem.clicked()
651+ Keys.onDeletePressed: currentItem.removed()
652+ Keys.onEscapePressed: done()
653 }
654
655 Item {
656@@ -106,8 +116,8 @@
657 top: parent.top
658 topMargin: -units.gu(0.7)
659 }
660- icon: expandedHistoryView.model.lastVisitedIcon
661- title: expandedHistoryView.model.domain
662+ icon: model ? model.lastVisitedIcon : ""
663+ title: model ? model.domain : ""
664 url: i18n.tr("%1 page", "%1 pages", entriesListView.count).arg(entriesListView.count)
665 enabled: false
666 }
667
668=== modified file 'src/app/webbrowser/HistoryView.qml'
669--- src/app/webbrowser/HistoryView.qml 2015-12-22 18:15:49 +0000
670+++ src/app/webbrowser/HistoryView.qml 2016-02-11 08:27:04 +0000
671@@ -1,5 +1,5 @@
672 /*
673- * Copyright 2014-2015 Canonical Ltd.
674+ * Copyright 2014-2016 Canonical Ltd.
675 *
676 * This file is part of webbrowser-app.
677 *
678@@ -22,7 +22,7 @@
679 import webbrowserapp.private 0.1
680 import "." as Local
681
682-Item {
683+FocusScope {
684 id: historyView
685
686 signal seeMoreEntriesClicked(var model)
687@@ -48,12 +48,14 @@
688 id: domainsListView
689 objectName: "domainsListView"
690
691+ focus: true
692+ currentIndex: 0
693+
694 anchors {
695 top: topBar.bottom
696 left: parent.left
697 right: parent.right
698 bottom: toolbar.top
699- rightMargin: units.gu(2)
700 }
701
702 model: SortFilterModel {
703@@ -77,6 +79,8 @@
704 width: parent.width
705 height: units.gu(5)
706
707+ readonly property int modelIndex: index
708+
709 title: model.domain
710 url: lastVisitedTitle
711 icon: model.lastVisitedIcon
712@@ -96,6 +100,12 @@
713 }
714 }
715 }
716+
717+ highlight: ListViewHighlight {}
718+
719+ Keys.onEnterPressed: currentItem.clicked()
720+ Keys.onReturnPressed: currentItem.clicked()
721+ Keys.onDeletePressed: currentItem.removed()
722 }
723
724 Local.Toolbar {
725
726=== modified file 'src/app/webbrowser/ListViewHighlight.qml'
727--- src/app/webbrowser/ListViewHighlight.qml 2016-01-28 16:48:27 +0000
728+++ src/app/webbrowser/ListViewHighlight.qml 2016-02-11 08:27:04 +0000
729@@ -18,6 +18,7 @@
730
731 import QtQuick 2.4
732 import Ubuntu.Components 1.3
733+import Unity.InputInfo 0.1
734
735 Rectangle {
736 color: "transparent"
737@@ -26,6 +27,14 @@
738 color: UbuntuColors.orange
739 }
740 radius: units.gu(0.3)
741- visible: (ListView.view && ListView.view.activeFocus) ||
742- (GridView.view && GridView.view.activeFocus)
743+ visible: hasKeyboard &&
744+ ((ListView.view && ListView.view.activeFocus) ||
745+ (GridView.view && GridView.view.activeFocus))
746+
747+ readonly property bool hasKeyboard: keyboardModel.count > 0
748+
749+ InputDeviceModel {
750+ id: keyboardModel
751+ deviceFilter: InputInfo.Keyboard
752+ }
753 }
754
755=== modified file 'src/app/webbrowser/NewTabView.qml'
756--- src/app/webbrowser/NewTabView.qml 2015-12-15 11:17:58 +0000
757+++ src/app/webbrowser/NewTabView.qml 2016-02-11 08:27:04 +0000
758@@ -1,5 +1,5 @@
759 /*
760- * Copyright 2014-2015 Canonical Ltd.
761+ * Copyright 2014-2016 Canonical Ltd.
762 *
763 * This file is part of webbrowser-app.
764 *
765@@ -19,10 +19,11 @@
766 import QtQuick 2.4
767 import Qt.labs.settings 1.0
768 import Ubuntu.Components 1.3
769+import Ubuntu.Components.ListItems 1.3 as ListItems
770 import webbrowserapp.private 0.1
771 import "."
772
773-Item {
774+FocusScope {
775 id: newTabView
776
777 property Settings settingsObject
778@@ -51,6 +52,18 @@
779 seeMoreBookmarksView = false
780 }
781 }
782+
783+ function ensureCurrentItemVisible(container, currentItem) {
784+ if (container.activeFocus && currentItem) {
785+ var top = container.y + currentItem.mapToItem(container, 0, 0).y
786+ var height = currentItem.height
787+ if (top < flickable.contentY) {
788+ flickable.contentY = top
789+ } else if ((flickable.contentY + flickable.height) < (top + height)) {
790+ flickable.contentY = top + height - flickable.height
791+ }
792+ }
793+ }
794 }
795
796 Rectangle {
797@@ -59,160 +72,259 @@
798 }
799
800 Flickable {
801+ id: flickable
802 anchors.fill: parent
803- contentHeight: internal.seeMoreBookmarksView ?
804- bookmarksFolderListViewLoader.height + units.gu(6) :
805- contentColumn.height
806-
807- Column {
808- id: contentColumn
809+ contentHeight: contentScope.height
810+ focus: true
811+
812+ onActiveFocusChanged: {
813+ if (activeFocus) {
814+ contentScope.forceActiveFocus()
815+ }
816+ }
817+
818+ Behavior on contentY {
819+ UbuntuNumberAnimation {}
820+ }
821+
822+ FocusScope {
823+ id: contentScope
824 anchors {
825 left: parent.left
826 right: parent.right
827- rightMargin: units.gu(1.5)
828 }
829 height: childrenRect.height
830
831- Row {
832+ Item {
833+ id: bookmarkListHeader
834+ objectName: "bookmarkListHeader"
835 height: units.gu(6)
836 anchors {
837+ top: parent.top
838 left: parent.left
839- leftMargin: units.gu(1.5)
840 right: parent.right
841 }
842- spacing: units.gu(1.5)
843-
844- Icon {
845- id: starredIcon
846- color: "#dd4814"
847- name: "starred"
848-
849- height: units.gu(2)
850- width: height
851-
852+
853+ Row {
854 anchors {
855- leftMargin: units.gu(1)
856- topMargin: units.gu(1)
857- verticalCenter: moreButton.verticalCenter
858- }
859- }
860-
861- Label {
862- width: parent.width - starredIcon.width - moreButton.width - units.gu(3)
863- anchors.verticalCenter: moreButton.verticalCenter
864-
865- text: i18n.tr("Bookmarks")
866- fontSize: "small"
867- }
868-
869- Button {
870- id: moreButton
871- objectName: "bookmarks.moreButton"
872- height: parent.height - units.gu(2)
873-
874- anchors { top: parent.top; topMargin: units.gu(1) }
875-
876- strokeColor: UbuntuColors.darkGrey
877-
878- visible: internal.numberOfBookmarks > 4
879-
880- text: internal.seeMoreBookmarksView ? i18n.tr("Less") : i18n.tr("More")
881-
882- onClicked: internal.seeMoreBookmarksView = !internal.seeMoreBookmarksView
883- }
884- }
885-
886- Rectangle {
887- height: units.gu(0.1)
888+ fill: parent
889+ leftMargin: units.gu(2)
890+ rightMargin: units.gu(2)
891+ }
892+ spacing: units.gu(1.5)
893+
894+ Icon {
895+ id: starredIcon
896+ color: "#dd4814"
897+ name: "starred"
898+
899+ height: units.gu(2)
900+ width: height
901+
902+ anchors {
903+ leftMargin: units.gu(1)
904+ topMargin: units.gu(1)
905+ verticalCenter: moreButton.verticalCenter
906+ }
907+ }
908+
909+ Label {
910+ width: parent.width - starredIcon.width - moreButton.width - units.gu(3)
911+ anchors.verticalCenter: moreButton.verticalCenter
912+
913+ text: i18n.tr("Bookmarks")
914+ fontSize: "small"
915+ }
916+
917+ Button {
918+ id: moreButton
919+ objectName: "bookmarks.moreButton"
920+ height: parent.height - units.gu(2)
921+ anchors { top: parent.top; topMargin: units.gu(1) }
922+ activeFocusOnPress: false
923+
924+ strokeColor: UbuntuColors.darkGrey
925+ visible: internal.numberOfBookmarks > 4
926+ text: internal.seeMoreBookmarksView ? i18n.tr("Less") : i18n.tr("More")
927+
928+ onClicked: {
929+ internal.seeMoreBookmarksView = !internal.seeMoreBookmarksView
930+ bookmarkListHeader.focus = true
931+ }
932+ }
933+ }
934+
935+ Keys.onEnterPressed: moreButton.clicked()
936+ Keys.onReturnPressed: moreButton.clicked()
937+ Keys.onSpacePressed: moreButton.clicked()
938+
939+ Keys.onDownPressed: {
940+ if (internal.seeMoreBookmarksView) {
941+ bookmarksFolderListViewLoader.focus = true
942+ } else {
943+ bookmarkList.focus = true
944+ }
945+ }
946+
947+ onActiveFocusChanged: internal.ensureCurrentItemVisible(this, this)
948+ }
949+
950+ ListViewHighlight {
951+ anchors.fill: bookmarkListHeader
952+ visible: hasKeyboard && bookmarkListHeader.activeFocus
953+ }
954+
955+ ListItems.ThinDivider {
956+ id: bookmarkListDivider
957 anchors {
958+ top: bookmarkListHeader.bottom
959 left: parent.left
960- leftMargin: units.gu(1.5)
961+ leftMargin: units.gu(2)
962 right: parent.right
963+ rightMargin: units.gu(2)
964 }
965- color: "#d3d3d3"
966+ opacity: bookmarkListHeader.activeFocus ? 0 : 1
967 }
968
969 Loader {
970 id: bookmarksFolderListViewLoader
971-
972 anchors {
973+ top: bookmarkListDivider.bottom
974 left: parent.left
975 right: parent.right
976 }
977-
978- height: status == Loader.Ready ? item.height : 0
979-
980 active: internal.seeMoreBookmarksView
981+ height: active ? item.height : 0
982
983 sourceComponent: BookmarksFoldersView {
984+ focus: true
985+ interactive: false
986+
987 homeBookmarkUrl: newTabView.settingsObject.homepage
988
989 onBookmarkClicked: newTabView.bookmarkClicked(url)
990 onBookmarkRemoved: newTabView.bookmarkRemoved(url)
991+
992+ onCurrentItemChanged: internal.ensureCurrentItemVisible(bookmarksFolderListViewLoader, currentItem)
993+ onActiveFocusChanged: internal.ensureCurrentItemVisible(bookmarksFolderListViewLoader, currentItem)
994+ }
995+
996+ Keys.onUpPressed: {
997+ if (moreButton.visible) {
998+ bookmarkListHeader.focus = true
999+ } else {
1000+ event.accepted = false
1001+ }
1002 }
1003 }
1004
1005- Column {
1006- id: bookmarksColumn
1007+ Loader {
1008+ id: bookmarkList
1009 anchors {
1010+ top: bookmarkListDivider.bottom
1011 left: parent.left
1012 right: parent.right
1013 }
1014-
1015- opacity: internal.seeMoreBookmarksView ? 0.0 : 1.0
1016- Behavior on opacity { UbuntuNumberAnimation {} }
1017- visible: opacity > 0
1018-
1019- // Force the height to be updated when bookmarks are removed
1020- // in another new tab
1021- height: units.gu(5) * (Math.min(internal.bookmarksCountLimit, internal.numberOfBookmarks) + 1)
1022- spacing: 0
1023-
1024- UrlDelegate {
1025- objectName: "homepageBookmark"
1026- anchors {
1027- left: parent.left
1028- right: parent.right
1029- }
1030- height: units.gu(5)
1031-
1032- title: i18n.tr('Homepage')
1033-
1034- leadingActions: null
1035-
1036- url: newTabView.settingsObject.homepage
1037- onClicked: newTabView.bookmarkClicked(url)
1038+ active: !internal.seeMoreBookmarksView
1039+ height: active ? item.height : 0
1040+ focus: true
1041+
1042+ LimitProxyModel {
1043+ id: limitedBookmarksModel
1044+ sourceModel: BookmarksModel
1045+ limit: internal.bookmarksCountLimit
1046 }
1047
1048- UrlsList {
1049+ sourceComponent: ListView {
1050 objectName: "bookmarksList"
1051- anchors {
1052- left: parent.left
1053- right: parent.right
1054- }
1055-
1056- spacing: 0
1057- limit: internal.bookmarksCountLimit
1058-
1059- model: BookmarksModel
1060-
1061- onUrlClicked: newTabView.bookmarkClicked(url)
1062- onUrlRemoved: newTabView.bookmarkRemoved(url)
1063+ focus: true
1064+ interactive: false
1065+ readonly property real delegateHeight: units.gu(5)
1066+ height: count * delegateHeight
1067+
1068+ model: limitedBookmarksModel.count + 1
1069+
1070+ delegate: UrlDelegate {
1071+ objectName: (index == 0) ? "homepageBookmark" : "bookmark_%1".arg(index)
1072+ anchors {
1073+ left: parent.left
1074+ right: parent.right
1075+ }
1076+ height: delegateHeight
1077+ removable: index > 0
1078+
1079+ readonly property var data: BookmarksModel.count ? limitedBookmarksModel.get(index - 1) : null
1080+ icon: (index > 0) ? data.icon : ""
1081+ title: (index > 0) ? data.title : i18n.tr("Homepage")
1082+ url: (index > 0) ? data.url : newTabView.settingsObject.homepage
1083+
1084+ onClicked: newTabView.bookmarkClicked(url)
1085+ onRemoved: {
1086+ if (removable) {
1087+ newTabView.bookmarkRemoved(url)
1088+ }
1089+ }
1090+ }
1091+
1092+ highlight: ListViewHighlight {}
1093+
1094+ Keys.onEnterPressed: currentItem.clicked()
1095+ Keys.onReturnPressed: currentItem.clicked()
1096+ Keys.onDeletePressed: currentItem.removed()
1097+
1098+ // Setting 'interactive' to false to prevent flicks also disables
1099+ // keyboard navigation, so it needs to be manually implemented.
1100+ Keys.onUpPressed: {
1101+ var current = currentIndex
1102+ decrementCurrentIndex()
1103+ if (currentIndex == current) {
1104+ event.accepted = false
1105+ }
1106+ }
1107+ Keys.onDownPressed: {
1108+ var current = currentIndex
1109+ incrementCurrentIndex()
1110+ if (currentIndex == current) {
1111+ event.accepted = false
1112+ }
1113+ }
1114+
1115+ onCurrentItemChanged: internal.ensureCurrentItemVisible(bookmarkList, currentItem)
1116+ onActiveFocusChanged: internal.ensureCurrentItemVisible(bookmarkList, currentItem)
1117+ }
1118+
1119+ Keys.onUpPressed: {
1120+ if (moreButton.visible) {
1121+ bookmarkListHeader.focus = true
1122+ } else {
1123+ event.accepted = false
1124+ }
1125+ }
1126+ Keys.onDownPressed: {
1127+ if (topSitesGrid.visible) {
1128+ topSitesGrid.focus = true
1129+ } else {
1130+ event.accepted = false
1131+ }
1132 }
1133 }
1134
1135 Item {
1136- height: units.gu(6)
1137+ id: topSitesHeader
1138 anchors {
1139+ top: bookmarkList.bottom
1140 left: parent.left
1141- leftMargin: units.gu(1.5)
1142 right: parent.right
1143 }
1144+ visible: !internal.seeMoreBookmarksView
1145+ height: visible ? units.gu(6) : 0
1146
1147 Label {
1148 anchors {
1149 left: parent.left
1150+ leftMargin: units.gu(2)
1151 right: parent.right
1152+ rightMargin: units.gu(2)
1153 bottom: parent.bottom
1154 bottomMargin: units.gu(1)
1155 }
1156@@ -225,28 +337,28 @@
1157 }
1158 }
1159
1160- Rectangle {
1161- height: units.gu(0.1)
1162+ ListItems.ThinDivider {
1163+ id: topSitesDivider
1164 anchors {
1165+ top: topSitesHeader.bottom
1166 left: parent.left
1167 leftMargin: units.gu(2)
1168 right: parent.right
1169+ rightMargin: units.gu(2)
1170 }
1171- color: "#d3d3d3"
1172-
1173- opacity: internal.seeMoreBookmarksView ? 0.0 : 1.0
1174- Behavior on opacity { UbuntuNumberAnimation {} }
1175+ visible: topSitesHeader.visible
1176 }
1177
1178 Label {
1179 objectName: "notopsites"
1180
1181- height: units.gu(11)
1182 anchors {
1183+ top: topSitesDivider.bottom
1184 left: parent.left
1185 right: parent.right
1186 }
1187- visible: topSitesModel.count == 0
1188+ visible: !internal.seeMoreBookmarksView && (topSitesModel.count == 0)
1189+ height: visible ? units.gu(11) : 0
1190
1191 horizontalAlignment: Text.AlignHCenter
1192 verticalAlignment: Text.AlignVCenter
1193@@ -255,36 +367,26 @@
1194 color: UbuntuColors.darkGrey
1195 }
1196
1197- Item {
1198+ FocusScope {
1199+ id: topSitesGrid
1200 anchors {
1201+ top: topSitesDivider.bottom
1202 left: parent.left
1203 right: parent.right
1204-
1205- // The UrlPreviewGrid's highlight extends to the left of the
1206- // grid itself by a margin.
1207- // Since we are clipping the parent we need to prevent the
1208- // highlight from being clipped away at the left edge.
1209- // We do this by shifting the parent left and the contents right
1210- // by an amount equal to the highlight's margin.
1211- leftMargin: units.gu(2) - grid.horizontalMargin
1212-
1213- // The right margin should be 2gu, which is set on all cells
1214- // of the UrlPreviewGrid already. However the parent Column
1215- // has 1.5gu right margin, so we are compensating for that
1216- // here instead of removing it from the Column itself and
1217- // reassigning it to all Column children except this one.
1218- rightMargin: - contentColumn.anchors.rightMargin
1219 }
1220- height: grid.contentHeight + units.gu(1)
1221+ visible: !internal.seeMoreBookmarksView && (topSitesModel.count > 0)
1222+ height: visible ? grid.contentHeight + units.gu(1) : 0
1223 clip: true
1224
1225 UrlPreviewGrid {
1226 id: grid
1227 objectName: "topSitesList"
1228+ focus: true
1229 anchors {
1230 left: parent.left
1231- leftMargin: grid.horizontalMargin
1232+ leftMargin: units.gu(2)
1233 right: parent.right
1234+ rightMargin: units.gu(2)
1235 top: parent.top
1236 topMargin: units.gu(2)
1237 bottom: parent.bottom
1238@@ -298,9 +400,6 @@
1239 visible: opacity > 0
1240 interactive: false
1241
1242- // No highlight as this view doesn’t support keyboard navigation
1243- highlight: null
1244-
1245 model: LimitProxyModel {
1246 limit: 10
1247 sourceModel: topSitesModel
1248@@ -312,7 +411,23 @@
1249 HistoryModel.hide(url)
1250 PreviewManager.checkDelete(url)
1251 }
1252+
1253+ // Setting 'interactive' to false to prevent flicks also disables
1254+ // keyboard navigation, so it needs to be manually implemented.
1255+ Keys.onLeftPressed: moveCurrentIndexLeft()
1256+ Keys.onRightPressed: moveCurrentIndexRight()
1257+
1258+ onCurrentItemChanged: internal.ensureCurrentItemVisible(topSitesGrid, currentItem)
1259+ onActiveFocusChanged: internal.ensureCurrentItemVisible(topSitesGrid, currentItem)
1260+
1261+ onCountChanged: {
1262+ if (activeFocus && (count == 0)) {
1263+ bookmarkList.focus = true
1264+ }
1265+ }
1266 }
1267+
1268+ Keys.onUpPressed: bookmarkList.focus = true
1269 }
1270 }
1271 }
1272
1273=== modified file 'src/app/webbrowser/NewTabViewWide.qml'
1274--- src/app/webbrowser/NewTabViewWide.qml 2015-11-23 09:41:48 +0000
1275+++ src/app/webbrowser/NewTabViewWide.qml 2016-02-11 08:27:04 +0000
1276@@ -1,5 +1,5 @@
1277 /*
1278- * Copyright 2015 Canonical Ltd.
1279+ * Copyright 2015-2016 Canonical Ltd.
1280 *
1281 * This file is part of webbrowser-app.
1282 *
1283@@ -31,7 +31,6 @@
1284 signal bookmarkClicked(url url)
1285 signal bookmarkRemoved(url url)
1286 signal historyEntryClicked(url url)
1287- signal releasingKeyboardFocus()
1288
1289 Keys.onTabPressed: selectedIndex = (selectedIndex + 1) % 2
1290 Keys.onBacktabPressed: selectedIndex = Math.abs((selectedIndex - 1) % 2)
1291@@ -56,16 +55,9 @@
1292 BookmarksFoldersViewWide {
1293 id: bookmarksFoldersViewWide
1294
1295- Keys.onUpPressed: newTabViewWide.releasingKeyboardFocus()
1296 onBookmarkClicked: newTabViewWide.bookmarkClicked(url)
1297 onBookmarkRemoved: newTabViewWide.bookmarkRemoved(url)
1298
1299- // Relinquish focus as the presses and releases that compose the
1300- // drag will move the keyboard focus in a location unexpected
1301- // for the user. This way it will go back to the address bar and
1302- // the user can predictably resume keyboard interaction from there.
1303- onDragStarted: newTabViewWide.releasingKeyboardFocus()
1304-
1305 anchors {
1306 top: sectionsGroup.bottom
1307 left: parent.left
1308@@ -105,10 +97,8 @@
1309 onActivated: newTabViewWide.historyEntryClicked(url)
1310 onRemoved: {
1311 HistoryModel.hide(url)
1312- if (topSitesModel.count === 0) newTabViewWide.releasingKeyboardFocus()
1313 PreviewManager.checkDelete(url)
1314 }
1315- onReleasingKeyboardFocus: newTabViewWide.releasingKeyboardFocus()
1316 }
1317
1318 Scrollbar {
1319
1320=== modified file 'src/app/webbrowser/ToolbarAction.qml'
1321--- src/app/webbrowser/ToolbarAction.qml 2015-09-22 01:26:42 +0000
1322+++ src/app/webbrowser/ToolbarAction.qml 2016-02-11 08:27:04 +0000
1323@@ -28,6 +28,7 @@
1324
1325 opacity: enabled ? 1.0 : 0.3
1326 width: Math.max(label.paintedWidth, icon.width)
1327+ activeFocusOnPress: false
1328
1329 Item {
1330 anchors {
1331
1332=== modified file 'src/app/webbrowser/UrlDelegate.qml'
1333--- src/app/webbrowser/UrlDelegate.qml 2015-10-09 03:10:13 +0000
1334+++ src/app/webbrowser/UrlDelegate.qml 2016-02-11 08:27:04 +0000
1335@@ -1,5 +1,5 @@
1336 /*
1337- * Copyright 2014-2015 Canonical Ltd.
1338+ * Copyright 2014-2016 Canonical Ltd.
1339 *
1340 * This file is part of webbrowser-app.
1341 *
1342@@ -40,6 +40,8 @@
1343 verticalCenter: parent.verticalCenter
1344 left: parent.left
1345 leftMargin: units.gu(1.5)
1346+ right: parent.right
1347+ rightMargin: units.gu(1.5)
1348 }
1349 spacing: units.gu(1)
1350
1351@@ -60,12 +62,18 @@
1352 }
1353
1354 Column {
1355- width: urlDelegate.width - headerComponentLoader.width - iconContainer.width - parent.spacing
1356- height: parent.height
1357+ width: parent.width - headerComponentLoader.width - iconContainer.width - parent.spacing - (headerComponentLoader.sourceComponent ? parent.spacing : 0)
1358+ anchors {
1359+ top: parent.top
1360+ bottom: parent.bottom
1361+ }
1362
1363 Label {
1364 id: title
1365-
1366+ anchors {
1367+ left: parent.left
1368+ right: parent.right
1369+ }
1370 fontSize: "x-small"
1371 color: UbuntuColors.darkGrey
1372 wrapMode: Text.Wrap
1373@@ -75,7 +83,10 @@
1374
1375 Label {
1376 id: url
1377-
1378+ anchors {
1379+ left: parent.left
1380+ right: parent.right
1381+ }
1382 fontSize: "xx-small"
1383 color: UbuntuColors.darkGrey
1384 wrapMode: Text.Wrap
1385
1386=== modified file 'src/app/webbrowser/UrlPreviewDelegate.qml'
1387--- src/app/webbrowser/UrlPreviewDelegate.qml 2015-10-16 07:55:02 +0000
1388+++ src/app/webbrowser/UrlPreviewDelegate.qml 2016-02-11 08:27:04 +0000
1389@@ -117,12 +117,16 @@
1390 Component {
1391 id: contextMenuComponent
1392 ActionSelectionPopover {
1393+ objectName: "urlPreviewDelegate.contextMenu"
1394 grabDismissAreaEvents: true
1395 actions: ActionList {
1396 Action {
1397 objectName: "delete"
1398 text: i18n.tr("Remove")
1399- onTriggered: preview.removed()
1400+ onTriggered: {
1401+ preview.removed()
1402+ preview.GridView.view.forceActiveFocus()
1403+ }
1404 }
1405 }
1406 }
1407
1408=== modified file 'src/app/webbrowser/UrlPreviewGrid.qml'
1409--- src/app/webbrowser/UrlPreviewGrid.qml 2016-01-28 18:14:51 +0000
1410+++ src/app/webbrowser/UrlPreviewGrid.qml 2016-02-11 08:27:04 +0000
1411@@ -17,10 +17,7 @@
1412 */
1413
1414 import QtQuick 2.4
1415-import Qt.labs.settings 1.0
1416 import Ubuntu.Components 1.3
1417-import webbrowserapp.private 0.1
1418-import ".."
1419
1420 GridView {
1421 id: grid
1422@@ -33,9 +30,6 @@
1423
1424 signal activated(url url)
1425 signal removed(url url)
1426- signal releasingKeyboardFocus()
1427-
1428- currentIndex: 0
1429
1430 cellWidth: previewWidth + horizontalMargin * 2
1431 cellHeight: previewHeight + verticalMargin * 2 + units.gu(4) // height of text + favicon + margins in delegate
1432@@ -59,8 +53,9 @@
1433 }
1434
1435 highlight: Item {
1436- visible: GridView.view && GridView.view.activeFocus
1437+ visible: viewHighlight.hasKeyboard && GridView.view && GridView.view.activeFocus
1438 ListViewHighlight {
1439+ id: viewHighlight
1440 visible: true
1441 width: previewWidth + units.gu(2)
1442 height: previewHeight + units.gu(5)
1443@@ -75,15 +70,27 @@
1444
1445 Keys.onDeletePressed: removed(currentItem.url)
1446
1447- Keys.onLeftPressed: {
1448- var i = grid.currentIndex
1449- grid.moveCurrentIndexLeft()
1450- if (i === grid.currentIndex) grid.releasingKeyboardFocus()
1451- }
1452-
1453 Keys.onUpPressed: {
1454- var i = grid.currentIndex
1455- grid.moveCurrentIndexUp()
1456- if (i === grid.currentIndex) grid.releasingKeyboardFocus()
1457+ var current = currentIndex
1458+ moveCurrentIndexUp()
1459+ if (current == currentIndex) {
1460+ event.accepted = false
1461+ }
1462+ }
1463+ Keys.onDownPressed: {
1464+ var current = currentIndex
1465+ moveCurrentIndexDown()
1466+ if (currentIndex == current) {
1467+ event.accepted = false
1468+ }
1469+ }
1470+
1471+ Timer {
1472+ // Work around a weird issue with the use of a LimitProxyModel in a
1473+ // grid view, where the currentIndex is changed when populating the
1474+ // model.
1475+ running: true
1476+ interval: 1
1477+ onTriggered: grid.currentIndex = 0
1478 }
1479 }
1480
1481=== removed file 'src/app/webbrowser/UrlsList.qml'
1482--- src/app/webbrowser/UrlsList.qml 2015-10-08 14:47:13 +0000
1483+++ src/app/webbrowser/UrlsList.qml 1970-01-01 00:00:00 +0000
1484@@ -1,52 +0,0 @@
1485-/*
1486- * Copyright 2014-2015 Canonical Ltd.
1487- *
1488- * This file is part of webbrowser-app.
1489- *
1490- * webbrowser-app is free software; you can redistribute it and/or modify
1491- * it under the terms of the GNU General Public License as published by
1492- * the Free Software Foundation; version 3.
1493- *
1494- * webbrowser-app is distributed in the hope that it will be useful,
1495- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1496- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1497- * GNU General Public License for more details.
1498- *
1499- * You should have received a copy of the GNU General Public License
1500- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1501- */
1502-
1503-import QtQuick 2.4
1504-import Ubuntu.Components 1.3
1505-
1506-Column {
1507- id: urlsList
1508-
1509- property alias model: urlsListRepeater.model
1510- property int limit: -1
1511-
1512- signal urlClicked(url url)
1513- signal urlRemoved(url url)
1514-
1515- spacing: units.gu(1)
1516-
1517- Repeater {
1518- id: urlsListRepeater
1519-
1520- delegate: Loader {
1521- active: limit < 0 || index < limit
1522- sourceComponent: UrlDelegate{
1523- id: urlDelegate
1524- width: urlsList.width
1525- height: units.gu(5)
1526-
1527- icon: model ? model.icon : ""
1528- title: model.title ? model.title : model.url
1529- url: model ? model.url : ""
1530-
1531- onClicked: urlsList.urlClicked(model.url)
1532- onRemoved: urlsList.urlRemoved(model.url)
1533- }
1534- }
1535- }
1536-}
1537
1538=== modified file 'tests/autopilot/webbrowser_app/emulators/browser.py'
1539--- tests/autopilot/webbrowser_app/emulators/browser.py 2016-02-11 08:27:04 +0000
1540+++ tests/autopilot/webbrowser_app/emulators/browser.py 2016-02-11 08:27:04 +0000
1541@@ -600,7 +600,12 @@
1542 return self.select_single(UrlDelegate, objectName="homepageBookmark")
1543
1544 def get_bookmarks_list(self):
1545- return self.select_single(UrlsList, objectName="bookmarksList")
1546+ return self.select_single(objectName="bookmarksList")
1547+
1548+ def get_bookmark_delegates(self):
1549+ list = self.get_bookmarks_list()
1550+ return sorted(list.select_many(UrlDelegate),
1551+ key=lambda delegate: delegate.globalRect.y)
1552
1553 def get_top_sites_list(self):
1554 return self.select_single(UrlPreviewGrid, objectName="topSitesList")
1555@@ -666,16 +671,6 @@
1556 return [folder.name for folder in self.get_folders_list()]
1557
1558
1559-class UrlsList(uitk.UbuntuUIToolkitCustomProxyObjectBase):
1560-
1561- def get_delegates(self):
1562- return sorted(self.select_many(UrlDelegate),
1563- key=lambda delegate: delegate.globalRect.y)
1564-
1565- def get_urls(self):
1566- return [delegate.url for delegate in self.get_delegates()]
1567-
1568-
1569 class UrlPreviewGrid(uitk.UbuntuUIToolkitCustomProxyObjectBase):
1570
1571 def get_delegates(self):
1572@@ -742,13 +737,11 @@
1573 class BookmarksFoldersView(uitk.UbuntuUIToolkitCustomProxyObjectBase):
1574
1575 def get_delegates(self):
1576- return sorted(self.select_many("QQuickItem",
1577- objectName="bookmarkFolderDelegate"),
1578+ return sorted(self.select_many(objectName="bookmarkFolderDelegate"),
1579 key=lambda delegate: delegate.globalRect.y)
1580
1581 def get_folder_delegate(self, folder):
1582- return self.select_single("QQuickItem",
1583- objectName="bookmarkFolderDelegate",
1584+ return self.select_single(objectName="bookmarkFolderDelegate",
1585 folderName=folder)
1586
1587 def get_urls_from_folder(self, folder):
1588@@ -756,8 +749,7 @@
1589 key=lambda delegate: delegate.globalRect.y)
1590
1591 def get_header_from_folder(self, folder):
1592- return folder.wait_select_single("QQuickItem",
1593- objectName="bookmarkFolderHeader")
1594+ return folder.wait_select_single(objectName="bookmarkFolderHeader")
1595
1596
1597 class ContextMenuBase(uitk.UbuntuUIToolkitCustomProxyObjectBase):
1598
1599=== modified file 'tests/autopilot/webbrowser_app/tests/test_new_tab_view.py'
1600--- tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2016-01-14 14:43:02 +0000
1601+++ tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2016-02-11 08:27:04 +0000
1602@@ -238,8 +238,7 @@
1603 self.main_window.wait_until_page_loaded(url)
1604
1605 def test_open_bookmark(self):
1606- bookmarks = self.new_tab_view.get_bookmarks_list()
1607- bookmark = bookmarks.get_delegates()[1]
1608+ bookmark = self.new_tab_view.get_bookmark_delegates()[2]
1609 url = bookmark.url
1610 self.pointing_device.click_object(bookmark)
1611 self.new_tab_view.wait_until_destroyed()
1612@@ -264,9 +263,8 @@
1613 bookmarks = self.new_tab_view.get_bookmarks_list()
1614 top_sites = self.new_tab_view.get_top_sites_list()
1615 self.assertThat(top_sites.visible, Equals(True))
1616- # When the bookmarks list is collapsed, it shows a maximum of 4 entries
1617- self.assertThat(lambda: len(bookmarks.get_delegates()),
1618- Eventually(Equals(4)))
1619+ # When the bookmarks list is collapsed, it shows a maximum of 5 entries
1620+ self.assertThat(bookmarks.count, Eventually(Equals(5)))
1621 # When expanded, it shows all entries
1622 more_button = self.new_tab_view.get_bookmarks_more_button()
1623 self.assertThat(more_button.visible, Equals(True))
1624@@ -280,18 +278,17 @@
1625 # Collapse again
1626 self.assertThat(more_button.visible, Equals(True))
1627 self.pointing_device.click_object(more_button)
1628- self.assertThat(lambda: len(bookmarks.get_delegates()),
1629- Eventually(Equals(4)))
1630+ bookmarks = self.new_tab_view.get_bookmarks_list()
1631+ self.assertThat(bookmarks.count, Eventually(Equals(5)))
1632 self.assertThat(top_sites.visible, Eventually(Equals(True)))
1633
1634 def _remove_first_bookmark(self):
1635- bookmarks = self.new_tab_view.get_bookmarks_list()
1636- delegate = bookmarks.get_delegates()[0]
1637- url = delegate.url
1638- delegate.trigger_leading_action("leadingAction.delete",
1639- delegate.wait_until_destroyed)
1640- self.assertThat(lambda: bookmarks.get_urls()[0],
1641- Eventually(NotEquals(url)))
1642+ bookmark = self.new_tab_view.get_bookmark_delegates()[1]
1643+ url = bookmark.url
1644+ bookmark.trigger_leading_action("leadingAction.delete", lambda: None)
1645+ self.assertThat(
1646+ lambda: self.new_tab_view.get_bookmark_delegates()[1].url,
1647+ Eventually(NotEquals(url)))
1648
1649 def _remove_first_bookmark_from_folder(self, folder):
1650 folders = self.new_tab_view.get_bookmarks_folder_list_view()
1651@@ -314,14 +311,12 @@
1652
1653 def test_remove_bookmarks_when_collapsed(self):
1654 bookmarks = self.new_tab_view.get_bookmarks_list()
1655- self.assertThat(lambda: len(bookmarks.get_delegates()),
1656- Eventually(Equals(4)))
1657+ self.assertThat(bookmarks.count, Eventually(Equals(5)))
1658 more_button = self.new_tab_view.get_bookmarks_more_button()
1659 for i in range(3):
1660 self._remove_first_bookmark()
1661 self.assertThat(more_button.visible, Eventually(Equals(i < 1)))
1662- self.assertThat(len(bookmarks.get_delegates()),
1663- Equals(4 if (i < 2) else 3))
1664+ self.assertThat(bookmarks.count, Equals(5 if (i < 2) else 4))
1665
1666 def test_remove_bookmarks_when_expanded(self):
1667 more_button = self.new_tab_view.get_bookmarks_more_button()
1668
1669=== modified file 'tests/unittests/qml/CMakeLists.txt'
1670--- tests/unittests/qml/CMakeLists.txt 2015-12-17 16:32:26 +0000
1671+++ tests/unittests/qml/CMakeLists.txt 2016-02-11 08:27:04 +0000
1672@@ -35,6 +35,7 @@
1673 include_directories(
1674 ${webbrowser-common_SOURCE_DIR}
1675 ${webbrowser-app_SOURCE_DIR}
1676+ ${unity8_SOURCE_DIR}/plugins
1677 )
1678 target_link_libraries(${TEST}
1679 Qt5::Core
1680@@ -44,6 +45,7 @@
1681 Qt5::Quick
1682 Qt5::QuickTest
1683 Qt5::Sql
1684+ InputInfo
1685 )
1686 add_test(${TEST} ${XVFB_COMMAND} ${CMAKE_CURRENT_BINARY_DIR}/${TEST}
1687 -input ${CMAKE_CURRENT_SOURCE_DIR}
1688
1689=== modified file 'tests/unittests/qml/tst_BookmarksView.qml'
1690--- tests/unittests/qml/tst_BookmarksView.qml 2015-11-17 17:19:13 +0000
1691+++ tests/unittests/qml/tst_BookmarksView.qml 2016-02-11 08:27:04 +0000
1692@@ -1,5 +1,5 @@
1693 /*
1694- * Copyright 2015 Canonical Ltd.
1695+ * Copyright 2015-2016 Canonical Ltd.
1696 *
1697 * This file is part of webbrowser-app.
1698 *
1699@@ -17,19 +17,22 @@
1700 */
1701
1702 import QtQuick 2.4
1703+import QtQuick.Window 2.2
1704 import QtTest 1.0
1705 import "../../../src/app/webbrowser"
1706 import webbrowserapp.private 0.1
1707
1708-Item {
1709+FocusScope {
1710 id: root
1711
1712+ focus: true
1713 width: 300
1714 height: 500
1715
1716 BookmarksView {
1717 id: view
1718 anchors.fill: parent
1719+ focus: true
1720 homepageUrl: "http://example.com/homepage"
1721 }
1722
1723@@ -58,7 +61,7 @@
1724 function init() {
1725 BookmarksModel.databasePath = ":memory:"
1726 populate()
1727- view.forceActiveFocus()
1728+ verify(view.activeFocus)
1729 compare(bookmarkEntryClickedSpy.count, 0)
1730 compare(doneSpy.count, 0)
1731 compare(newTabClickedSpy.count, 0)
1732@@ -110,5 +113,81 @@
1733 swipeToDeleteAndConfirm(bookmark)
1734 tryCompare(BookmarksModel, "count", 2)
1735 }
1736+
1737+ function test_keyboard_navigation() {
1738+ var listview = findChild(view, "bookmarksFolderListView")
1739+ waitForRendering(listview)
1740+ verify(listview.activeFocus)
1741+
1742+ var firstHeader = findChild(listview, "bookmarkFolderHeader")
1743+ verify(firstHeader.activeFocus)
1744+ var firstFolder = firstHeader.parent
1745+ compare(firstFolder.folderName, "")
1746+ verify(firstFolder.expanded)
1747+
1748+ keyClick(Qt.Key_Up)
1749+ verify(firstHeader.activeFocus)
1750+
1751+ keyClick(Qt.Key_Space)
1752+ verify(!firstFolder.expanded)
1753+
1754+ keyClick(Qt.Key_Space)
1755+ verify(firstFolder.expanded)
1756+
1757+ keyClick(Qt.Key_Up)
1758+ verify(firstHeader.activeFocus)
1759+
1760+ keyClick(Qt.Key_Down)
1761+ verify(findChild(firstFolder, "urlDelegate_0").activeFocus)
1762+
1763+ keyClick(Qt.Key_Enter)
1764+ compare(bookmarkEntryClickedSpy.count, 1)
1765+ compare(bookmarkEntryClickedSpy.signalArguments[0][0], "http://example.com/homepage")
1766+
1767+ keyClick(Qt.Key_Down)
1768+ verify(findChild(firstFolder, "urlDelegate_1").activeFocus)
1769+
1770+ keyClick(Qt.Key_Return)
1771+ compare(bookmarkEntryClickedSpy.count, 2)
1772+ compare(bookmarkEntryClickedSpy.signalArguments[1][0], "http://example.com")
1773+
1774+ keyClick(Qt.Key_Delete)
1775+ compare(BookmarksModel.count, 2)
1776+ verify(findChild(firstFolder, "urlDelegate_0").activeFocus)
1777+
1778+ keyClick(Qt.Key_Down)
1779+ var secondHeader = root.Window.activeFocusItem
1780+ compare(secondHeader.objectName, "bookmarkFolderHeader")
1781+ var secondFolder = secondHeader.parent
1782+ compare(secondFolder.folderName, "FolderA")
1783+ verify(!secondFolder.expanded)
1784+
1785+ keyClick(Qt.Key_Down)
1786+ var thirdHeader = root.Window.activeFocusItem
1787+ compare(thirdHeader.objectName, "bookmarkFolderHeader")
1788+ var thirdFolder = thirdHeader.parent
1789+ compare(thirdFolder.folderName, "FolderB")
1790+ verify(!thirdFolder.expanded)
1791+
1792+ keyClick(Qt.Key_Down)
1793+ verify(thirdHeader.activeFocus)
1794+
1795+ keyClick(Qt.Key_Delete)
1796+ verify(thirdHeader.activeFocus)
1797+
1798+ keyClick(Qt.Key_Space)
1799+ verify(thirdFolder.expanded)
1800+
1801+ keyClick(Qt.Key_Down)
1802+ verify(findChild(thirdFolder, "urlDelegate_0").activeFocus)
1803+
1804+ keyClick(Qt.Key_Delete)
1805+ compare(BookmarksModel.count, 1)
1806+ verify(!thirdFolder.active)
1807+ verify(secondFolder.activeFocus)
1808+
1809+ keyClick(Qt.Key_Space)
1810+ verify(secondFolder.expanded)
1811+ }
1812 }
1813 }
1814
1815=== modified file 'tests/unittests/qml/tst_HistoryView.qml'
1816--- tests/unittests/qml/tst_HistoryView.qml 2015-12-17 16:32:26 +0000
1817+++ tests/unittests/qml/tst_HistoryView.qml 2016-02-11 08:27:04 +0000
1818@@ -1,5 +1,5 @@
1819 /*
1820- * Copyright 2015 Canonical Ltd.
1821+ * Copyright 2015-2016 Canonical Ltd.
1822 *
1823 * This file is part of webbrowser-app.
1824 *
1825@@ -17,17 +17,19 @@
1826 */
1827
1828 import QtQuick 2.4
1829+import QtQuick.Window 2.2
1830 import QtTest 1.0
1831 import "../../../src/app/webbrowser"
1832 import webbrowserapp.private 0.1
1833
1834-Item {
1835+FocusScope {
1836 id: root
1837
1838+ focus: true
1839 width: 300
1840 height: 500
1841
1842- readonly property var historyView: historyViewLoader.item
1843+ readonly property Item historyView: historyViewLoader.item
1844
1845 Loader {
1846 id: historyViewLoader
1847@@ -65,8 +67,9 @@
1848 HistoryModel.databasePath = ":memory:"
1849 populate()
1850 historyViewLoader.active = true
1851+ historyView.loadModel()
1852 waitForRendering(historyView)
1853- historyView.loadModel()
1854+ verify(historyView.activeFocus)
1855 var domainsList = findChild(historyView, "domainsListView")
1856 waitForRendering(domainsList)
1857 tryCompare(domainsList, "count", 3)
1858@@ -82,6 +85,7 @@
1859 }
1860
1861 function cleanup() {
1862+ historyViewLoader.active = false
1863 HistoryModel.databasePath = ""
1864 seeMoreEntriesClickedSpy.clear()
1865 doneSpy.clear()
1866@@ -185,5 +189,47 @@
1867 clickItem(deleteButton)
1868 tryCompare(HistoryModel, "count", 0)
1869 }
1870+
1871+ function test_keyboard_navigation() {
1872+ var listview = findChild(historyView, "domainsListView")
1873+ verify(listview.activeFocus)
1874+ var domains = getListItems(listview, "historyViewDomainDelegate")
1875+
1876+ function check_current(index) {
1877+ compare(listview.currentIndex, index)
1878+ var current = root.Window.activeFocusItem
1879+ compare(current.objectName, "historyViewDomainDelegate")
1880+ compare(current.modelIndex, index)
1881+ compare(current.title, domains[index].title)
1882+ verify(current.activeFocus)
1883+ return current
1884+ }
1885+
1886+ var current = check_current(0)
1887+
1888+ keyClick(Qt.Key_Up)
1889+ verify(current.activeFocus)
1890+
1891+ keyClick(Qt.Key_Enter)
1892+ compare(seeMoreEntriesClickedSpy.count, 1)
1893+ compare(seeMoreEntriesClickedSpy.signalArguments[0][0].domain, domains[0].title)
1894+
1895+ keyClick(Qt.Key_Down)
1896+ current = check_current(1)
1897+
1898+ keyClick(Qt.Key_Return)
1899+ compare(seeMoreEntriesClickedSpy.count, 2)
1900+ compare(seeMoreEntriesClickedSpy.signalArguments[1][0].domain, domains[1].title)
1901+
1902+ keyClick(Qt.Key_Down)
1903+ current = check_current(2)
1904+
1905+ keyClick(Qt.Key_Down)
1906+ verify(current.activeFocus)
1907+
1908+ keyClick(Qt.Key_Delete)
1909+ tryCompare(listview, 'currentIndex', 1)
1910+ current = check_current(1)
1911+ }
1912 }
1913 }
1914
1915=== added file 'tests/unittests/qml/tst_NewTabView.qml'
1916--- tests/unittests/qml/tst_NewTabView.qml 1970-01-01 00:00:00 +0000
1917+++ tests/unittests/qml/tst_NewTabView.qml 2016-02-11 08:27:04 +0000
1918@@ -0,0 +1,292 @@
1919+/*
1920+ * Copyright 2016 Canonical Ltd.
1921+ *
1922+ * This file is part of webbrowser-app.
1923+ *
1924+ * webbrowser-app is free software; you can redistribute it and/or modify
1925+ * it under the terms of the GNU General Public License as published by
1926+ * the Free Software Foundation; version 3.
1927+ *
1928+ * webbrowser-app is distributed in the hope that it will be useful,
1929+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1930+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1931+ * GNU General Public License for more details.
1932+ *
1933+ * You should have received a copy of the GNU General Public License
1934+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1935+ */
1936+
1937+import QtQuick 2.4
1938+import QtQuick.Window 2.2
1939+import QtTest 1.0
1940+import Qt.labs.settings 1.0
1941+import "../../../src/app/webbrowser"
1942+import webbrowserapp.private 0.1
1943+
1944+FocusScope {
1945+ id: root
1946+
1947+ focus: true
1948+ width: 350
1949+ height: 500
1950+
1951+ Loader {
1952+ id: newTabViewLoader
1953+ anchors.fill: parent
1954+ focus: true
1955+ active: false
1956+ sourceComponent: NewTabView {
1957+ focus: true
1958+ anchors.fill: parent
1959+ settingsObject: Settings {
1960+ property url homepage: "http://example.com/homepage"
1961+ }
1962+ }
1963+ }
1964+
1965+ readonly property Item view: newTabViewLoader.item
1966+
1967+ SignalSpy {
1968+ id: bookmarkClickedSpy
1969+ target: view
1970+ signalName: "bookmarkClicked"
1971+ }
1972+
1973+ SignalSpy {
1974+ id: bookmarkRemovedSpy
1975+ target: view
1976+ signalName: "bookmarkRemoved"
1977+ }
1978+
1979+ SignalSpy {
1980+ id: historyEntryClickedSpy
1981+ target: view
1982+ signalName: "historyEntryClicked"
1983+ }
1984+
1985+ SignalSpy {
1986+ id: countChangedSpy
1987+ signalName: "countChanged"
1988+ }
1989+
1990+ WebbrowserTestCase {
1991+ name: "NewTabView"
1992+ when: windowShown
1993+
1994+ function init() {
1995+ HistoryModel.databasePath = ":memory:"
1996+ BookmarksModel.databasePath = ":memory:"
1997+ populate()
1998+ newTabViewLoader.active = true
1999+ waitForRendering(view)
2000+ verify(view.activeFocus)
2001+ compare(bookmarkClickedSpy.count, 0)
2002+ compare(bookmarkRemovedSpy.count, 0)
2003+ compare(historyEntryClickedSpy.count, 0)
2004+ }
2005+
2006+ function populate() {
2007+ HistoryModel.add("http://example.com/foo", "foo", "")
2008+ HistoryModel.add("http://example.org/bar", "bar", "")
2009+ HistoryModel.add("http://example.net/baz", "baz", "")
2010+ HistoryModel.add("http://example.net/qux", "qux", "")
2011+ HistoryModel.add("http://example.net/norf", "norf", "")
2012+ compare(HistoryModel.count, 5)
2013+
2014+ BookmarksModel.add("http://example.com", "Example", "", "")
2015+ BookmarksModel.add("http://example.org/a", "Example a", "", "FolderA")
2016+ BookmarksModel.add("http://example.org/b", "Example b", "", "FolderB")
2017+ BookmarksModel.add("http://example.org/c", "Example c", "", "FolderC")
2018+ BookmarksModel.add("http://example.org/d", "Example d", "", "FolderD")
2019+ compare(BookmarksModel.count, 5)
2020+ }
2021+
2022+ function cleanup() {
2023+ newTabViewLoader.active = false
2024+ BookmarksModel.databasePath = ""
2025+ HistoryModel.databasePath = ""
2026+ bookmarkClickedSpy.clear()
2027+ bookmarkRemovedSpy.clear()
2028+ historyEntryClickedSpy.clear()
2029+ }
2030+
2031+ function verify_bookmarks_expanded(expected) {
2032+ if (expected) {
2033+ compare(findChild(view, "bookmarksList"), null)
2034+ compare(findChild(view, "bookmarksFolderListView").objectName,
2035+ "bookmarksFolderListView")
2036+ } else {
2037+ compare(findChild(view, "bookmarksList").objectName,
2038+ "bookmarksList")
2039+ compare(findChild(view, "bookmarksFolderListView"), null)
2040+ }
2041+ }
2042+
2043+ function check_focused_item(objectName) {
2044+ var focused = root.Window.activeFocusItem
2045+ verify(focused !== null)
2046+ compare(focused.objectName, objectName)
2047+ return focused
2048+ }
2049+
2050+ function test_click_bookmark() {
2051+ var listview = findChild(view, "bookmarksList")
2052+
2053+ var homepage = findChild(listview, "homepageBookmark")
2054+ clickItem(homepage)
2055+ compare(bookmarkClickedSpy.count, 1)
2056+ compare(bookmarkClickedSpy.signalArguments[0][0],
2057+ view.settingsObject.homepage)
2058+
2059+ var bookmark = findChild(listview, "bookmark_1")
2060+ clickItem(bookmark)
2061+ compare(bookmarkClickedSpy.count, 2)
2062+ compare(bookmarkClickedSpy.signalArguments[1][0], bookmark.url)
2063+ }
2064+
2065+ function test_delete_bookmark() {
2066+ var listview = findChild(view, "bookmarksList")
2067+ var bookmark = findChild(listview, "bookmark_2")
2068+ swipeToDeleteAndConfirm(bookmark)
2069+ bookmarkRemovedSpy.wait()
2070+ compare(bookmarkRemovedSpy.count, 1)
2071+ compare(bookmarkRemovedSpy.signalArguments[0][0], bookmark.url)
2072+ }
2073+
2074+ function test_expand_bookmarks() {
2075+ verify_bookmarks_expanded(false)
2076+ var button = findChild(view, "bookmarks.moreButton")
2077+ clickItem(button)
2078+ verify_bookmarks_expanded(true)
2079+ clickItem(button)
2080+ verify_bookmarks_expanded(false)
2081+ }
2082+
2083+ function test_click_top_site() {
2084+ var grid = findChild(view, "topSitesList")
2085+ compare(grid.count, 5)
2086+ var topsites = getListItems(grid, "topSiteItem")
2087+ compare(topsites.length, 5)
2088+ var topsite = topsites[3]
2089+ clickItem(topsite)
2090+ compare(historyEntryClickedSpy.count, 1)
2091+ compare(historyEntryClickedSpy.signalArguments[0][0], topsite.url)
2092+ }
2093+
2094+ function test_delete_top_site() {
2095+ var grid = findChild(view, "topSitesList")
2096+ compare(grid.count, 5)
2097+ var topsite = getListItems(grid, "topSiteItem")[1]
2098+ clickItem(topsite, Qt.RightButton)
2099+ var contextMenu = findChild(root, "urlPreviewDelegate.contextMenu")
2100+ var action = findChild(contextMenu, "delete_button")
2101+ clickItem(action)
2102+ compare(grid.count, 4)
2103+ }
2104+
2105+ function test_keyboard_navigation() {
2106+ var bookmarksList = findChild(view, "bookmarksList")
2107+ verify(bookmarksList.activeFocus)
2108+ check_focused_item("homepageBookmark")
2109+
2110+ keyClick(Qt.Key_Up)
2111+ check_focused_item("bookmarkListHeader")
2112+
2113+ verify_bookmarks_expanded(false)
2114+ keyClick(Qt.Key_Enter)
2115+ verify_bookmarks_expanded(true)
2116+ keyClick(Qt.Key_Return)
2117+ verify_bookmarks_expanded(false)
2118+ keyClick(Qt.Key_Space)
2119+ verify_bookmarks_expanded(true)
2120+ keyClick(Qt.Key_Enter)
2121+ verify_bookmarks_expanded(false)
2122+
2123+ keyClick(Qt.Key_Up)
2124+ check_focused_item("bookmarkListHeader")
2125+
2126+ keyClick(Qt.Key_Down)
2127+ check_focused_item("homepageBookmark")
2128+
2129+ keyClick(Qt.Key_Delete)
2130+ compare(bookmarkRemovedSpy.count, 0)
2131+
2132+ keyClick(Qt.Key_Enter)
2133+ compare(bookmarkClickedSpy.count, 1)
2134+ compare(bookmarkClickedSpy.signalArguments[0][0],
2135+ view.settingsObject.homepage)
2136+
2137+ keyClick(Qt.Key_Down)
2138+ var bookmark = check_focused_item("bookmark_1")
2139+
2140+ keyClick(Qt.Key_Delete)
2141+ compare(bookmarkRemovedSpy.count, 1)
2142+ compare(bookmarkRemovedSpy.signalArguments[0][0], bookmark.url)
2143+
2144+ keyClick(Qt.Key_Down)
2145+ keyClick(Qt.Key_Down)
2146+ keyClick(Qt.Key_Down)
2147+ check_focused_item("bookmark_4")
2148+
2149+ keyClick(Qt.Key_Down)
2150+ var grid = findChild(view, "topSitesList")
2151+ verify(grid.activeFocus)
2152+ compare(grid.currentIndex, 0)
2153+ var topsite = check_focused_item("topSiteItem")
2154+
2155+ keyClick(Qt.Key_Enter)
2156+ compare(historyEntryClickedSpy.count, 1)
2157+ compare(historyEntryClickedSpy.signalArguments[0][0], topsite.url)
2158+
2159+ keyClick(Qt.Key_Return)
2160+ compare(historyEntryClickedSpy.count, 2)
2161+ compare(historyEntryClickedSpy.signalArguments[1][0], topsite.url)
2162+
2163+ keyClick(Qt.Key_Right)
2164+ compare(grid.currentIndex, 1)
2165+ check_focused_item("topSiteItem")
2166+
2167+ keyClick(Qt.Key_Down)
2168+ compare(grid.currentIndex, 3)
2169+ check_focused_item("topSiteItem")
2170+
2171+ keyClick(Qt.Key_Down)
2172+ compare(grid.currentIndex, 3)
2173+ check_focused_item("topSiteItem")
2174+
2175+ keyClick(Qt.Key_Left)
2176+ compare(grid.currentIndex, 2)
2177+ check_focused_item("topSiteItem")
2178+
2179+ keyClick(Qt.Key_Down)
2180+ compare(grid.currentIndex, 4)
2181+ check_focused_item("topSiteItem")
2182+
2183+ keyClick(Qt.Key_Down)
2184+ compare(grid.currentIndex, 4)
2185+ check_focused_item("topSiteItem")
2186+
2187+ keyClick(Qt.Key_Left)
2188+ compare(grid.currentIndex, 3)
2189+ check_focused_item("topSiteItem")
2190+
2191+ var notopsiteslabel = findChild(view, "notopsites")
2192+ verify(!notopsiteslabel.visible)
2193+ compare(grid.count, 5)
2194+ countChangedSpy.target = grid
2195+ for (var i = 4; i >= 0; --i) {
2196+ keyClick(Qt.Key_Delete)
2197+ countChangedSpy.wait()
2198+ compare(grid.count, i)
2199+ compare(grid.currentIndex, i - 1)
2200+ }
2201+ compare(grid.count, 0)
2202+ verify(notopsiteslabel.visible)
2203+ bookmarksList = findChild(view, "bookmarksList")
2204+ verify(bookmarksList.activeFocus)
2205+
2206+ keyClick(Qt.Key_Down)
2207+ verify(bookmarksList.activeFocus)
2208+ }
2209+ }
2210+}
2211
2212=== modified file 'tests/unittests/qml/tst_NewTabViewWide.qml'
2213--- tests/unittests/qml/tst_NewTabViewWide.qml 2015-11-17 17:16:37 +0000
2214+++ tests/unittests/qml/tst_NewTabViewWide.qml 2016-02-11 08:27:04 +0000
2215@@ -1,5 +1,5 @@
2216 /*
2217- * Copyright 2015 Canonical Ltd.
2218+ * Copyright 2015-2016 Canonical Ltd.
2219 *
2220 * This file is part of webbrowser-app.
2221 *
2222@@ -43,11 +43,6 @@
2223 }
2224
2225 SignalSpy {
2226- id: releasingKeyboardFocusSpy
2227- signalName: "releasingKeyboardFocus"
2228- }
2229-
2230- SignalSpy {
2231 id: historyEntryClickedSpy
2232 signalName: "historyEntryClicked"
2233 }
2234@@ -76,8 +71,6 @@
2235
2236 view.focus = true
2237
2238- releasingKeyboardFocusSpy.target = view
2239- releasingKeyboardFocusSpy.clear()
2240 historyEntryClickedSpy.target = view
2241 historyEntryClickedSpy.clear()
2242 bookmarkClickedSpy.target = view
2243@@ -163,10 +156,8 @@
2244 compare(list.currentIndex, 0)
2245 keyClick(Qt.Key_Up)
2246 compare(list.currentIndex, 0)
2247- compare(releasingKeyboardFocusSpy.count, 1)
2248 keyClick(Qt.Key_Left)
2249 compare(list.currentIndex, 0)
2250- compare(releasingKeyboardFocusSpy.count, 2)
2251 }
2252
2253 function test_activate_topsites_by_keyboard() {
2254
2255=== modified file 'tests/unittests/qml/tst_QmlTests.cpp'
2256--- tests/unittests/qml/tst_QmlTests.cpp 2015-12-17 16:32:26 +0000
2257+++ tests/unittests/qml/tst_QmlTests.cpp 2016-02-11 08:27:04 +0000
2258@@ -1,5 +1,5 @@
2259 /*
2260- * Copyright 2013-2015 Canonical Ltd.
2261+ * Copyright 2013-2016 Canonical Ltd.
2262 *
2263 * This file is part of webbrowser-app.
2264 *
2265@@ -37,13 +37,7 @@
2266 #include "searchengine.h"
2267 #include "tabs-model.h"
2268 #include "text-search-filter-model.h"
2269-
2270-static QObject* FileOperations_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine)
2271-{
2272- Q_UNUSED(engine);
2273- Q_UNUSED(scriptEngine);
2274- return new FileOperations();
2275-}
2276+#include "Unity/InputInfo/qdeclarativeinputdevicemodel_p.h"
2277
2278 class TestContext : public QObject
2279 {
2280@@ -126,60 +120,51 @@
2281 };
2282
2283 class HistoryModelMock : public HistoryModel {
2284- Q_OBJECT
2285+ Q_OBJECT
2286
2287 public:
2288- static bool compareHistoryEntries(const HistoryEntry& a, const HistoryEntry& b) {
2289- return a.lastVisit < b.lastVisit;
2290- }
2291-
2292- Q_INVOKABLE int addByDate(const QUrl& url, const QString& title, const QDateTime& date)
2293- {
2294- int index = getEntryIndex(url);
2295- int visitsToAdd = 1;
2296- if (index == -1) {
2297- add(url, title, QString());
2298- index = getEntryIndex(url);
2299- visitsToAdd = 0;
2300- }
2301-
2302- // Since this is useful only for testing and efficiency is not critical
2303- // we reorder the model and reset it every time we add a new item by date
2304- // to keep things simple.
2305- beginResetModel();
2306- HistoryEntry entry = m_entries.takeAt(index);
2307- entry.lastVisit = date;
2308- entry.visits = entry.visits + visitsToAdd;
2309- m_entries.append(entry);
2310- std::sort(m_entries.begin(), m_entries.end(), compareHistoryEntries);
2311- endResetModel();
2312-
2313- updateExistingEntryInDatabase(entry);
2314-
2315- return entry.visits;
2316- }
2317+ static bool compareHistoryEntries(const HistoryEntry& a, const HistoryEntry& b) {
2318+ return a.lastVisit < b.lastVisit;
2319+ }
2320+
2321+ Q_INVOKABLE int addByDate(const QUrl& url, const QString& title, const QDateTime& date)
2322+ {
2323+ int index = getEntryIndex(url);
2324+ int visitsToAdd = 1;
2325+ if (index == -1) {
2326+ add(url, title, QString());
2327+ index = getEntryIndex(url);
2328+ visitsToAdd = 0;
2329+ }
2330+
2331+ // Since this is useful only for testing and efficiency is not critical
2332+ // we reorder the model and reset it every time we add a new item by date
2333+ // to keep things simple.
2334+ beginResetModel();
2335+ HistoryEntry entry = m_entries.takeAt(index);
2336+ entry.lastVisit = date;
2337+ entry.visits = entry.visits + visitsToAdd;
2338+ m_entries.append(entry);
2339+ std::sort(m_entries.begin(), m_entries.end(), compareHistoryEntries);
2340+ endResetModel();
2341+
2342+ updateExistingEntryInDatabase(entry);
2343+
2344+ return entry.visits;
2345+ }
2346 };
2347
2348-static QObject* TestContext_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine)
2349-{
2350- Q_UNUSED(engine);
2351- Q_UNUSED(scriptEngine);
2352- return new TestContext();
2353-}
2354-
2355-static QObject* BookmarksModel_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine)
2356-{
2357- Q_UNUSED(engine);
2358- Q_UNUSED(scriptEngine);
2359- return new BookmarksModel();
2360-}
2361-
2362-static QObject* HistoryModel_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine)
2363-{
2364- Q_UNUSED(engine);
2365- Q_UNUSED(scriptEngine);
2366- return new HistoryModelMock();
2367-}
2368+#define MAKE_SINGLETON_FACTORY(type) \
2369+ static QObject* type##_singleton_factory(QQmlEngine* engine, QJSEngine* scriptEngine) { \
2370+ Q_UNUSED(engine); \
2371+ Q_UNUSED(scriptEngine); \
2372+ return new type(); \
2373+ }
2374+
2375+MAKE_SINGLETON_FACTORY(FileOperations)
2376+MAKE_SINGLETON_FACTORY(BookmarksModel)
2377+MAKE_SINGLETON_FACTORY(HistoryModelMock)
2378+MAKE_SINGLETON_FACTORY(TestContext)
2379
2380 int main(int argc, char** argv)
2381 {
2382@@ -191,7 +176,7 @@
2383 qmlRegisterType<TabsModel>(browserUri, 0, 1, "TabsModel");
2384 qmlRegisterSingletonType<BookmarksModel>(browserUri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory);
2385 qmlRegisterType<BookmarksFolderListModel>(browserUri, 0, 1, "BookmarksFolderListModel");
2386- qmlRegisterSingletonType<HistoryModel>(browserUri, 0, 1, "HistoryModel", HistoryModel_singleton_factory);
2387+ qmlRegisterSingletonType<HistoryModel>(browserUri, 0, 1, "HistoryModel", HistoryModelMock_singleton_factory);
2388 qmlRegisterType<HistoryDomainModel>(browserUri, 0, 1, "HistoryDomainModel");
2389 qmlRegisterType<HistoryDomainListModel>(browserUri, 0, 1, "HistoryDomainListModel");
2390 qmlRegisterType<HistoryLastVisitDateListModel>(browserUri, 0, 1, "HistoryLastVisitDateListModel");
2391@@ -202,6 +187,10 @@
2392 const char* testUri = "webbrowsertest.private";
2393 qmlRegisterSingletonType<TestContext>(testUri, 0, 1, "TestContext", TestContext_singleton_factory);
2394
2395+ const char* inputInfoUri = "Unity.InputInfo";
2396+ qmlRegisterType<QDeclarativeInputDeviceModel>(inputInfoUri, 0, 1, "InputDeviceModel");
2397+ qmlRegisterType<QInputDevice>(inputInfoUri, 0, 1, "InputInfo");
2398+
2399 return quick_test_main(argc, argv, "QmlTests", nullptr);
2400 }
2401

Subscribers

People subscribed via source and target branches

to status/vote changes: