on xenial help sign in
update header in dash to use the new header from the SDK Fixes: 1335491

Approved by: Michał Sawicz

commitd56d8b419624f07407d898adbbce370842b0edf8 authorMichael Zanetti <michael.zanetti@canonical.com> dateWed 9 Jul 2014, 19:43 (12 years ago) parent1d141f9641283b
27 files changed+432−834
debian/control+3−3 qml/Components/PageHeader.qml+216−315 qml/Components/PageHeaderLabel.qml−41 qml/Components/SearchHistoryModel.qml → qml/Components/SearchHistoryModel/SearchHistoryModel.qml+1 qml/Components/SearchHistoryModel/qmldir+1 qml/Components/graphics/headerback@27.png qml/Dash/Dash.qml−3 qml/Dash/DashContent.qml+1−63 qml/Dash/GenericScopeView.qml+20−27 qml/Dash/PreviewListView.qml+3−20 qml/Dash/ScopeItem.qml+4−39 qml/Dash/graphics/icon_listview_clear@20.png qml/Hud/HudParametrizedActionsPage.qml+2−2 qml/Panel/Panel.qml+1−31 qml/ScopeTool.qml−1 qml/Shell.qml−1 tests/autopilot/unity8/shell/emulators/dash.py+27−24 tests/autopilot/unity8/shell/emulators/main_window.py−5 tests/autopilot/unity8/shell/tests/test_emulators.py−1 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Components/tst_PageHeaderLabel.qml → tests/qmltests/Components/tst_PageHeader.qml+87−54 tests/qmltests/Components/tst_PageHeader/logo-ubuntu-orange.svg+60 tests/qmltests/Dash/tst_Dash.qml−39 tests/qmltests/Dash/tst_DashContent.qml+5−67 tests/qmltests/Dash/tst_GenericScopeView.qml−26 tests/qmltests/Panel/tst_Panel.qml−35 tests/qmltests/tst_Shell.qml−36
debian/control+3−3
@@ -40,7 +40,7 @@ Build-Depends: cmake,
4040 qtdeclarative5-private-dev (>= 5.2.1),
4141 qtdeclarative5-qtmultimedia-plugin,
4242 qtdeclarative5-ubuntu-settings-components (>= 0.3),
43− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.48) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.48),
43+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
4444 ttf-ubuntu-font-family,
4545Standards-Version: 3.9.4
4646Homepage: http://launchpad.net/unity
@@ -53,7 +53,7 @@ Vcs-Browser: https://bazaar.launchpad.net/~unity-team/unity8/trunk
5353Package: indicators-client
5454Architecture: amd64 armhf i386
5555Depends: qmenumodel-qml (>= 0.2.7),
56− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.48) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.48),
56+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
5757 unity8 (= ${binary:Version}),
5858 ${misc:Depends},
5959 ${shlibs:Depends},
@@ -101,7 +101,7 @@ Description: Unity 8 shell
101101Package: unity8-common
102102Architecture: all
103103Depends: qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
104− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.48) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.48),
104+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 0.1.49) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 0.1.49),
105105 qtdeclarative5-unity-notifications-plugin | unity-notifications-impl,
106106 ubuntu-thumbnailer-impl-0,
107107 unity-application-impl-2,
qml/Components/PageHeader.qml+216−315
@@ -14,386 +14,287 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Ubuntu.Components.Popups 0.1
20−import Ubuntu.Components.ListItems 0.1 as ListItem
21−import Unity 0.2
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Ubuntu.Components.Themes.Ambiance 1.1
20+import Ubuntu.Components.Popups 1.0
21+import Ubuntu.Components.ListItems 1.0
22+import "SearchHistoryModel"
2223
2324Item {
2425 id: root
26+ objectName: "pageHeader"
27+ implicitHeight: headerContainer.height + units.gu(2) + bottomContainer.height
28+
29+ property bool showBackButton: false
30+ property string title
31+ property string imageSource
32+
2533 property bool searchEntryEnabled: false
26− property alias searchQuery: searchField.text
27− property ListModel searchHistory
28− property var scope: null
29− property alias childItem: itemContainer.children
30− property alias bottomItem: bottomContainer.children
31− property alias showBackButton: backButton.visible
34+ property ListModel searchHistory: SearchHistoryModel
35+ property alias searchQuery: searchTextField.text
36+ property bool searchInProgress: false
3237
33− signal backClicked
38+ property alias bottomItem: bottomContainer.children
3439
35− height: header.height + units.gu(2) + bottomContainer.height
36− implicitHeight: header.height + units.gu(2) + bottomContainer.height
40+ signal backClicked()
3741
3842 function triggerSearch() {
39− if (searchEntryEnabled) searchField.forceActiveFocus()
43+ if (searchEntryEnabled) {
44+ headerContainer.showSearch = true;
45+ searchTextField.forceActiveFocus();
46+ }
4047 }
4148
42− function resetSearch() {
43− if (!searchHistory) return;
44−
45− searchHistory.addQuery(searchField.text);
46− unfocus();
47− searchField.text = "";
49+ function resetSearch(keepFocus) {
50+ if (searchHistory) {
51+ searchHistory.addQuery(searchTextField.text);
52+ }
53+ if (!keepFocus) {
54+ unfocus();
55+ }
56+ searchTextField.text = "";
57+ if (headerContainer.popover != null) {
58+ PopupUtils.close(headerContainer.popover);
59+ }
4860 }
4961
5062 function unfocus() {
51− searchField.focus = false;
63+ searchTextField.focus = false;
64+ if (!searchTextField.text) {
65+ headerContainer.showSearch = false;
66+ }
5267 }
5368
54− Connections {
55− target: greeter
56− onShownChanged: if (shown) resetSearch()
69+ function openSearchHistory() {
70+ if (openSearchAnimation.running) {
71+ openSearchAnimation.openSearchHistory = true;
72+ } else if (root.searchHistory.count > 0 && headerContainer.popover == null) {
73+ headerContainer.popover = PopupUtils.open(popoverComponent, searchTextField,
74+ {
75+ "contentWidth": searchTextField.width,
76+ "edgeMargins": units.gu(1)
77+ }
78+ );
79+ }
5780 }
5881
59− Flickable {
60− id: header
61− anchors {
62− left: parent.left
63− right: parent.right
64− top: parent.top
82+ onImageSourceChanged: {
83+ if (imageSource) {
84+ header.contents = imageComponent.createObject();
85+ } else {
86+ header.contents.destroy();
87+ header.contents = null;
6588 }
66− height: units.gu(6.5)
67−
68− interactive: false
69− contentHeight: headerContainer.height
70− clip: true
71−
72− contentY: searchField.activeFocus || searchField.text != "" ? searchContainer.y : headerContainer.y
73−
74− Behavior on contentY { NumberAnimation { duration: 200; easing.type: Easing.OutQuad } }
89+ }
7590
76− AbstractButton {
77− id: backButton
78− objectName: root.objectName + "_backButton"
79− visible: false
80− height: header.height
81− y: header.contentY
82− anchors {
83− left: parent.left
84− leftMargin: visible ? units.gu(2) : 0
91+ InverseMouseArea {
92+ anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + bottomContainer.height }
93+ visible: headerContainer.showSearch
94+ onPressed: {
95+ if (headerContainer.popover) {
96+ PopupUtils.close(headerContainer.popover);
8597 }
86− width: visible ? image.width + units.gu(2) : 0
87− onClicked: root.backClicked();
88− Image {
89− id: image
90− anchors.centerIn: parent
91− source: "graphics/headerback.png"
98+ if (!searchTextField.text) {
99+ headerContainer.showSearch = false;
92100 }
101+ searchTextField.focus = false;
102+ mouse.accepted = false;
93103 }
104+ }
94105
95− // FIXME this could potentially be simplified to avoid all the containers
96− Item {
97− id: headerContainer
98−
99− anchors {
100− left: backButton.right
101− right: parent.right
102− }
103− height: childrenRect.height
104−
105− Item {
106− id: itemContainer
107−
108− width: searchContainer.narrowMode ? header.width : header.width - searchContainer.width
109− height: header.height
110− }
111−
112− Item {
113− id: searchContainer
114− objectName: "searchContainer"
115−
116− visible: searchEntryEnabled
117− property bool popoverShouldOpen: false
118− property bool popoverShouldClose: false
119−
120− property bool narrowMode: parent.width < units.gu(80)
121−
122− property bool active: searchField.text != "" || searchField.activeFocus
123− property var popover: null
106+ Flickable {
107+ id: headerContainer
108+ objectName: "headerContainer"
109+ clip: true
110+ anchors { left: parent.left; top: parent.top; right: parent.right }
111+ height: units.gu(6.5)
112+ contentHeight: headersColumn.height
113+ interactive: false
114+ contentY: showSearch ? 0 : height
124115
125− anchors.right: headerContainer.right
126− height: header.height
116+ property bool showSearch: false
117+ property var popover: null
127118
128− state:
129− if (active && narrowMode) "narrowActive"
130− else if (!active && narrowMode) "narrowInactive"
131− else if (active && !narrowMode) "active"
132− else if (!active && !narrowMode) "inactive"
119+ Behavior on contentY {
120+ UbuntuNumberAnimation {
121+ id: openSearchAnimation
122+ property bool openSearchHistory: false
133123
134− onStateChanged: {
135− if (state == "active" || state == "narrowActive") {
136− popoverShouldOpen = true;
137− popoverShouldClose = false;
138− } else {
139− popoverShouldOpen = false;
140− popoverShouldClose = true;
124+ onRunningChanged: {
125+ if (!running && openSearchAnimation.openSearchHistory) {
126+ openSearchAnimation.openSearchHistory = false;
127+ root.openSearchHistory();
141128 }
142129 }
130+ }
131+ }
143132
144− function openPopover() {
145− if (searchHistory.count > 0) {
146− searchContainer.popover = PopupUtils.open(popoverComponent, pointerPositioner,
147− {
148− "contentWidth": searchField.width,
149− "edgeMargins": units.gu(1)
150− }
151− )
133+ Column {
134+ id: headersColumn
135+ anchors { left: parent.left; right: parent.right }
136+
137+ PageHeadStyle {
138+ id: searchHeader
139+ anchors { left: parent.left; right: parent.right }
140+ height: headerContainer.height
141+ contentHeight: height
142+ separatorSource: ""
143+ // Required to keep PageHeadStyle noise down as it expects the Page's properties around.
144+ property var styledItem: searchHeader
145+ property string title
146+ property var config: PageHeadConfiguration {
147+ backAction: Action {
148+ iconName: "back"
149+ onTriggered: {
150+ root.resetSearch();
151+ headerContainer.showSearch = false;
152+ }
152153 }
153− popoverShouldOpen = false;
154− popoverShouldClose = false;
155154 }
156−
157− function closePopover() {
158− if (searchContainer.popover) {
159− PopupUtils.close(searchContainer.popover);
160− searchContainer.popover = null;
155+ property var contents: TextField {
156+ id: searchTextField
157+ objectName: "searchTextField"
158+ hasClearButton: false
159+ anchors {
160+ fill: parent
161+ leftMargin: units.gu(1)
162+ topMargin: units.gu(1)
163+ bottomMargin: units.gu(1)
164+ rightMargin: root.width > units.gu(60) ? root.width - units.gu(40) : units.gu(1)
161165 }
162− popoverShouldOpen = false;
163− popoverShouldClose = false;
164− }
165−
166− onActiveFocusChanged: if (!activeFocus) { searchHistory.addQuery(searchField.text) }
167−
168− TextField {
169− id: searchField
170166
171− anchors.fill: parent
172− anchors.margins: units.gu(1)
173−
174− inputMethodHints: Qt.ImhNoPredictiveText
175− hasClearButton: false
167+ secondaryItem: AbstractButton {
168+ height: searchTextField.height
169+ width: height
170+ enabled: searchTextField.text.length > 0
176171
177− primaryItem: AbstractButton {
178− enabled: searchField.text != "" && !searchIndicator.running
179− onClicked: {
180− if (searchField.text != "") {
181− searchHistory.addQuery(searchField.text)
182− searchField.text = ""
172+ Image {
173+ objectName: "clearIcon"
174+ anchors.fill: parent
175+ anchors.margins: units.gu(.75)
176+ source: "image://theme/clear"
177+ opacity: searchTextField.text.length > 0 && !searchActivityIndicator.running
178+ visible: opacity > 0
179+ Behavior on opacity {
180+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
183181 }
184182 }
185− height: parent.height
186− width: height
187183
188184 ActivityIndicator {
189− id: searchIndicator
185+ id: searchActivityIndicator
190186 objectName: "searchIndicator"
191−
192− anchors {
193− verticalCenter: parent.verticalCenter
194− left: parent.left
195− leftMargin: units.gu(0.5)
187+ anchors.fill: parent
188+ anchors.margins: units.gu(.75)
189+ running: root.searchInProgress
190+ opacity: running ? 1 : 0
191+ Behavior on opacity {
192+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
196193 }
197−
198− running: opacity > 0
199− }
200−
201− Image {
202− id: primaryImage
203− objectName: "primaryImage"
204− anchors {
205− verticalCenter: parent.verticalCenter
206− left: parent.left
207− leftMargin: units.gu(0.5)
208− }
209− width: units.gu(3)
210− height: units.gu(3)
211− visible: opacity > 0
212194 }
213195
214− Item {
215− id: pointerPositioner
216− anchors.left: parent.right
217− anchors.leftMargin: units.gu(0.5)
218− anchors.top: parent.bottom
196+ onClicked: {
197+ root.resetSearch(true);
198+ root.openSearchHistory();
219199 }
220200 }
221201
222− onTextChanged: {
223− if (text != "") searchContainer.closePopover()
224− else if (text == "" && activeFocus) searchContainer.openPopover()
225− }
226−
227202 onActiveFocusChanged: {
228− if (!activeFocus) searchContainer.closePopover()
203+ if (activeFocus) {
204+ root.openSearchHistory();
205+ }
229206 }
207+ }
208+ }
230209
231− states: [
232− State {
233− name: "searching"
234− when: scope && scope.searchInProgress
235− PropertyChanges { target: searchIndicator; running: true; opacity: 1 }
236− PropertyChanges { target: primaryImage; opacity: 0 }
237− },
238− State {
239− name: "idle"
240− when: !scope || !scope.searchInProgress
241− PropertyChanges { target: searchIndicator; opacity: 0 }
242− PropertyChanges { target: primaryImage; opacity: 1 }
210+ PageHeadStyle {
211+ id: header
212+ objectName: "innerPageHeader"
213+ anchors { left: parent.left; right: parent.right }
214+ height: headerContainer.height
215+ contentHeight: height
216+ separatorSource: ""
217+ // FIXME: Use the theme's color once we're split from the shell
218+ textColor: "grey"
219+ property var styledItem: header
220+ property string title: root.title
221+ property var config: PageHeadConfiguration {
222+ backAction: Action {
223+ iconName: "back"
224+ visible: root.showBackButton
225+ onTriggered: {
226+ root.backClicked();
243227 }
244− ]
228+ }
245229
246− transitions: [
247− Transition {
248− to: "searching"
249− reversible: true
250− SequentialAnimation {
251− NumberAnimation { target: primaryImage; property: "opacity"; duration: UbuntuAnimation.FastDuration; easing.type: Easing.Linear }
252− NumberAnimation { target: searchIndicator; property: "opacity"; duration: UbuntuAnimation.FastDuration; easing.type: Easing.Linear }
230+ actions: [
231+ Action {
232+ iconName: "search"
233+ visible: root.searchEntryEnabled
234+ onTriggered: {
235+ headerContainer.showSearch = true;
236+ searchTextField.forceActiveFocus();
253237 }
254238 }
255239 ]
256240 }
257241
258− states: [
259− State {
260− name: "wide"
261− AnchorChanges { target: itemContainer; anchors.top: headerContainer.top }
262− AnchorChanges { target: searchContainer; anchors.left: undefined; anchors.top: itemContainer.top }
263− },
264− State {
265− name: "narrow"
266− PropertyChanges { target: searchField; highlighted: true }
267− AnchorChanges { target: itemContainer; anchors.top: searchContainer.bottom }
268− AnchorChanges { target: searchContainer; anchors.left: headerContainer.left; anchors.top: headerContainer.top }
269− },
270− State {
271− name: "active"
272− extend: "wide"
273− PropertyChanges { target: searchContainer; width: units.gu(40) }
274− PropertyChanges { target: primaryImage; source: searchField.text ? "../Dash/graphics/icon_clear.png" : "../Dash/graphics/icon_search_active.png" }
275− PropertyChanges { target: searchField; highlighted: true }
276− },
277− State {
278− name: "inactive"
279− extend: "wide"
280− PropertyChanges { target: searchContainer; width: units.gu(25) }
281− PropertyChanges { target: primaryImage; source: "../Dash/graphics/icon_search_inactive.png" }
282− PropertyChanges { target: searchField; highlighted: false }
283− },
284− State {
285− name: "narrowActive"
286− extend: "narrow"
287− PropertyChanges { target: header; contentY: 0 }
288− PropertyChanges { target: primaryImage; source: searchField.text ? "../Dash/graphics/icon_clear.png" : "../Dash/graphics/icon_search_active.png" }
289− },
290− State {
291− name: "narrowInactive"
292− extend: "narrow"
293− PropertyChanges { target: header; contentY: header.height }
294− PropertyChanges { target: primaryImage; source: searchField.text ? "../Dash/graphics/icon_clear.png" : "../Dash/graphics/icon_search_active.png" }
242+ property var contents: null
243+ Component.onCompleted: {
244+ if (root.imageSource) {
245+ header.contents = imageComponent.createObject();
295246 }
296− ]
297−
298− transitions: [
299− Transition {
300− to: "active"
301− SequentialAnimation {
302− ParallelAnimation {
303− NumberAnimation { targets: [searchContainer, searchField]; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
304− PropertyAction { target: primaryImage; property: "source" }
305− AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
306− }
307− ScriptAction { script: if (searchContainer.popoverShouldOpen) { searchContainer.openPopover(); } }
308− }
309− },
310− Transition {
311− to: "inactive"
312− ScriptAction { script: if (searchContainer.popoverShouldClose) { searchContainer.closePopover(); } }
313− NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.InOutQuad }
314− AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
315− },
316− Transition {
317− to: "narrowActive"
318− SequentialAnimation {
319− ParallelAnimation {
320− NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
321− AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
322− }
323− ScriptAction { script: if (searchContainer.popoverShouldOpen) { searchContainer.openPopover(); } }
324− }
325− },
326− Transition {
327− to: "narrowInactive"
328− ScriptAction { script: if (searchContainer.popoverShouldClose) { searchContainer.closePopover(); } }
329− NumberAnimation { targets: [searchContainer, searchField] ; property: "width"; duration: 200; easing.type: Easing.OutQuad }
330− AnchorAnimation { targets: [searchContainer, itemContainer]; duration: 200; easing.type: Easing.InOutQuad }
331− }
332− ]
333−
247+ }
334248 Component {
335− id: popoverComponent
336− Popover {
337− id: popover
338−
339− // FIXME: this should go into the first item below, but enable: false
340− // prevents mouse events propagation
341− AbstractButton {
342− anchors {
343− top: parent.top
344− right: parent.right
345− }
346− height: units.gu(6)
347− width: height
348−
349− onClicked: searchContainer.closePopover()
249+ id: imageComponent
350250
351− Image {
352− anchors.centerIn: parent
353− width: units.gu(2)
354− height: units.gu(2)
355− source: "../Dash/graphics/icon_listview_clear.png"
356− }
251+ Item {
252+ anchors { fill: parent; topMargin: units.gu(1); bottomMargin: units.gu(1) }
253+ clip: true
254+ Image {
255+ objectName: "titleImage"
256+ anchors.fill: parent
257+ source: root.imageSource
258+ fillMode: Image.PreserveAspectFit
259+ horizontalAlignment: Image.AlignLeft
260+ sourceSize.height: height
357261 }
262+ }
263+ }
264+ }
265+ }
266+ }
358267
359− Column {
360− anchors {
361− top: parent.top
362− left: parent.left
363− right: parent.right
364− }
365−
366− ListItem.Standard { enabled: false; text: i18n.tr("Recent searches") }
268+ Component {
269+ id: popoverComponent
270+ Popover {
271+ id: popover
272+ autoClose: false
367273
368− Repeater {
369− id: recentSearches
370− model: searchHistory
274+ Component.onDestruction: {
275+ headerContainer.popover = null;
276+ }
371277
372− delegate: ListItem.Standard {
373− showDivider: index < recentSearches.count - 1
374− text: query
375− onClicked: {
376− searchHistory.addQuery(text)
377− searchField.text = text
378− }
379− }
380− }
381− }
382− }
278+ Column {
279+ anchors {
280+ top: parent.top
281+ left: parent.left
282+ right: parent.right
383283 }
384284
385− InverseMouseArea {
386− enabled: searchField.activeFocus
285+ Repeater {
286+ id: recentSearches
287+ model: searchHistory
387288
388− anchors {
389− top: parent.top
390− left: parent.left
391− right: parent.right
289+ delegate: Standard {
290+ showDivider: index < recentSearches.count - 1
291+ text: query
292+ onClicked: {
293+ searchHistory.addQuery(text);
294+ searchTextField.text = text;
295+ PopupUtils.close(popover);
296+ }
392297 }
393−
394− height: searchContainer.popover ? parent.height + searchContainer.popover.contentHeight + units.gu(2) : parent.height
395−
396− onPressed: searchField.focus = false
397298 }
398299 }
399300 }
@@ -402,7 +303,7 @@ Item {
402303 BorderImage {
403304 id: bottomBorder
404305 anchors {
405− top: header.bottom
306+ top: headerContainer.bottom
406307 left: parent.left
407308 right: parent.right
408309 bottom: bottomContainer.top
qml/Components/PageHeaderLabel.qmldeleted−41
@@ -1,41 +0,0 @@
1−/*
2− * Copyright (C) 2013 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−
20−PageHeader {
21− property alias text: label.text
22−
23− childItem: Label {
24− id: label
25− anchors {
26− left: parent.left
27− leftMargin: units.gu(2)
28− right: parent.right
29− verticalCenter: parent.verticalCenter
30− }
31−
32− color: Theme.palette.selected.backgroundText
33− opacity: 0.8
34− font.family: "Ubuntu"
35− font.weight: Font.Light
36− fontSize: "x-large"
37− elide: Text.ElideRight
38− style: Text.Raised
39− styleColor: "black"
40− }
41−}
qml/Components/SearchHistoryModel.qml → qml/Components/SearchHistoryModel/SearchHistoryModel.qmlrenamed+1
@@ -14,6 +14,7 @@
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1515 */
1616
17+pragma Singleton
1718import QtQuick 2.0
1819
1920// TODO sanitize input, move to persistent storage
qml/Components/SearchHistoryModel/qmldirnew file+1
@@ -0,0 +1 @@
1+singleton SearchHistoryModel 1.0 SearchHistoryModel.qml
qml/Components/graphics/headerback@27.pngdeleted

Binary file not shown.

qml/Dash/Dash.qml−3
@@ -26,7 +26,6 @@ Showable {
2626
2727 visible: shown
2828
29− property ListModel searchHistory: SearchHistoryModel {}
3029 property bool searchable: !dashContent.previewOpen && !scopeItem.previewOpen
3130
3231 property string showScopeOnLoaded: "clickscope"
@@ -76,7 +75,6 @@ Showable {
7675 height: parent.height
7776 model: filteredScopes
7877 scopes: scopes
79− searchHistory: dash.searchHistory
8078 visible: x != -width
8179 onGotoScope: {
8280 dash.setCurrentScope(scopeId, true, false);
@@ -110,7 +108,6 @@ Showable {
110108 anchors.left: dashContent.right
111109 width: parent.width
112110 height: parent.height
113− searchHistory: dash.searchHistory
114111 scale: dash.contentScale
115112 clip: scale != 1.0
116113 visible: scope != null
qml/Dash/DashContent.qml+1−63
@@ -28,8 +28,6 @@ Item {
2828 readonly property alias currentIndex: dashContentList.currentIndex
2929 property alias previewOpen: previewListView.open
3030
31− property ListModel searchHistory
32−
3331 signal scopeLoaded(string scopeId)
3432 signal gotoScope(string scopeId)
3533 signal openScope(var scope)
@@ -147,11 +145,10 @@ Item {
147145
148146 onLoaded: {
149147 item.objectName = scope.id
150− item.pageHeader = dashPageHeader;
151148 item.previewListView = previewListView;
152149 item.scope = Qt.binding(function() { return scope })
153150 item.isCurrent = Qt.binding(function() { return visible && ListView.isCurrentItem })
154− item.tabBarHeight = Qt.binding(function() { return dashPageHeader.implicitHeight; })
151+ item.title = Qt.binding(function() { return dashContentList.model.get(index).title; })
155152 dashContent.scopeLoaded(item.scope.id)
156153 }
157154 Connections {
@@ -168,64 +165,6 @@ Item {
168165 Component.onDestruction: active = false
169166 }
170167 }
171−
172− PageHeader {
173− id: dashPageHeader
174− objectName: "pageHeader"
175− width: parent.width
176− searchEntryEnabled: true
177− searchHistory: dashContent.searchHistory
178− scope: dashContentList.currentItem && dashContentList.currentItem.theScope
179−
180− childItem: TabBar {
181− id: tabBar
182− objectName: "tabbar"
183− height: units.gu(6.5)
184− width: parent.width
185− style: DashContentTabBarStyle {}
186−
187− SortFilterProxyModel {
188− id: tabBarModel
189−
190− model: dashContentList.model
191−
192− property int selectedIndex: -1
193− onSelectedIndexChanged: {
194− if (dashContentList.currentIndex == -1 && tabBar.selectedIndex != -1) {
195− // TODO This together with the Timer below
196− // are a workaround for the first tab sometimes not showing the text.
197− // But Tabs are going away in the future so not sure if makes
198− // sense invetigating what's the problem at this stage
199− selectionModeTimer.restart();
200− }
201− dashContentList.currentIndex = selectedIndex;
202− }
203− }
204−
205− model: tabBarModel.count > 0 ? tabBarModel : null
206−
207− Connections {
208− target: dashContentList
209− onCurrentIndexChanged: {
210− tabBarModel.selectedIndex = dashContentList.currentIndex
211− }
212− }
213−
214− Timer {
215− id: selectionModeTimer
216− interval: 1
217− onTriggered: tabBar.selectionMode = false
218− }
219− }
220−
221− bottomItem: DashDepartments {
222− scope: dashContentList.currentItem ? dashContentList.currentItem.theScope : null
223− width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
224− anchors.right: parent.right
225− windowHeight: dashContent.height
226− windowWidth: dashContent.width
227− }
228− }
229168 }
230169
231170 PreviewListView {
@@ -233,7 +172,6 @@ Item {
233172 objectName: "dashContentPreviewList"
234173 visible: x != width
235174 scope: dashContentList.currentItem ? dashContentList.currentItem.theScope : null
236− pageHeader: dashPageHeader
237175 width: parent.width
238176 height: parent.height
239177 anchors.left: dashContentListHolder.right
qml/Dash/GenericScopeView.qml+20−27
@@ -29,13 +29,14 @@ FocusScope {
2929 property SortFilterProxyModel categories: categoryFilter
3030 property bool isCurrent: false
3131 property alias moving: categoryView.moving
32− property int tabBarHeight: 0
33− property PageHeader pageHeader: null
3432 property Item previewListView: null
35−
33+ property string title
34+ property bool hasBackAction: false
3635 property bool enableHeightBehaviorOnNextCreation: false
3736 property var categoryView: categoryView
3837
38+ signal backClicked()
39+
3940 onScopeChanged: {
4041 if (scope) {
4142 scope.activateApplication.connect(activateApp);
@@ -88,14 +89,6 @@ FocusScope {
8889 when: isCurrent
8990 }
9091
91− Connections {
92− target: panel
93− onSearchClicked: if (isCurrent) {
94− pageHeader.triggerSearch()
95− categoryView.showHeader()
96− }
97− }
98−
9992 Connections {
10093 target: scopeView.scope
10194 onShowDash: previewListView.open = false;
@@ -111,10 +104,6 @@ FocusScope {
111104
112105 property string expandedCategoryId: ""
113106
114− onContentYChanged: pageHeader.positionRealHeader();
115− onOriginYChanged: pageHeader.positionRealHeader();
116− onContentHeightChanged: pageHeader.positionRealHeader();
117−
118107 delegate: ListItems.Base {
119108 id: baseItem
120109 objectName: "dashCategory" + category
@@ -319,20 +308,24 @@ FocusScope {
319308 categoryView.expandedCategoryId = "";
320309 }
321310 }
322− pageHeader: Item {
323− implicitHeight: scopeView.tabBarHeight
324− onHeightChanged: {
325− if (scopeView.pageHeader && scopeView.isCurrent) {
326− scopeView.pageHeader.height = height;
327− }
328− }
329− onYChanged: positionRealHeader();
330311
331− function positionRealHeader() {
332− if (scopeView.pageHeader && scopeView.isCurrent) {
333− scopeView.pageHeader.y = y + parent.y;
334− }
312+ pageHeader: PageHeader {
313+ id: pageHeader
314+ width: parent.width
315+ title: scopeView.title
316+ showBackButton: scopeView.hasBackAction
317+ searchEntryEnabled: true
318+ searchInProgress: scopeView.scope.searchInProgress
319+
320+ bottomItem: DashDepartments {
321+ scope: scopeView.scope
322+ width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
323+ anchors.right: parent.right
324+ windowHeight: scopeView.height
325+ windowWidth: scopeView.width
335326 }
327+
328+ onBackClicked: scopeView.backClicked()
336329 }
337330 }
338331}
qml/Dash/PreviewListView.qml+3−20
@@ -24,7 +24,6 @@ Item {
2424 id: root
2525
2626 property var scope: null
27− property var pageHeader: null
2827
2928 property alias open: previewListView.open
3029 property alias model: previewListView.model
@@ -36,27 +35,11 @@ Item {
3635 id: header
3736 objectName: root.objectName + "_pageHeader"
3837 width: parent.width
39− searchEntryEnabled: false
40− scope: root.scope
41− height: units.gu(8.5)
38+ title: scope.name
4239 showBackButton: true
43− onBackClicked: root.open = false
40+ searchEntryEnabled: false
4441
45− childItem: Label {
46− id: label
47− anchors {
48− left: parent.left
49− right: parent.right
50− verticalCenter: parent.verticalCenter
51− }
52− text: scope ? scope.name : ""
53− // TODO Saviq: These should come from updated Ubuntu Palette.
54− color: "#888888"
55− font.family: "Ubuntu"
56− font.weight: Font.Light
57− fontSize: "x-large"
58− elide: Text.ElideRight
59− }
42+ onBackClicked: root.open = false
6043 }
6144
6245 ListView {
qml/Dash/ScopeItem.qml+4−39
@@ -25,8 +25,6 @@ Item {
2525 property alias scope: scopeView.scope
2626 property alias previewOpen: previewListView.open
2727
28− property ListModel searchHistory
29−
3028 signal back
3129 signal gotoScope(string scopeId)
3230 signal openScope(var scope)
@@ -48,56 +46,23 @@ Item {
4846 width: parent.width
4947 height: parent.height
5048 isCurrent: scope != null
51− tabBarHeight: scopeItemPageHeader.implicitHeight
52− pageHeader: scopeItemPageHeader
49+ title: scope ? scope.name : ""
50+ hasBackAction: true
5351 previewListView: previewListView
5452
53+ onBackClicked: root.back();
54+
5555 Connections {
5656 target: scopeView.isCurrent ? scope : null
5757 onGotoScope: root.gotoScope(scopeId);
5858 onOpenScope: root.openScope(scope);
5959 }
6060 }
61−
62− PageHeader {
63− id: scopeItemPageHeader
64− width: parent.width
65− searchEntryEnabled: true
66− searchHistory: root.searchHistory
67− scope: root.scope
68− height: units.gu(8.5)
69− showBackButton: true
70− onBackClicked: root.back();
71−
72− childItem: Label {
73− id: label
74− anchors {
75− left: parent.left
76− right: parent.right
77− verticalCenter: parent.verticalCenter
78− }
79− text: scope ? scope.name : ""
80− color: "#888888"
81− font.family: "Ubuntu"
82− font.weight: Font.Light
83− fontSize: "x-large"
84− elide: Text.ElideRight
85− }
86−
87− bottomItem: DashDepartments {
88− scope: root.scope
89− width: parent.width <= units.gu(60) ? parent.width : units.gu(40)
90− anchors.right: parent.right
91− windowHeight: root.height
92− windowWidth: root.width
93− }
94− }
9561 }
9662
9763 PreviewListView {
9864 id: previewListView
9965 visible: x != width
100− pageHeader: scopeItemPageHeader
10166 scope: root.scope
10267 width: parent.width
10368 height: parent.height
qml/Dash/graphics/icon_listview_clear@20.pngdeleted

Binary file not shown.

qml/Hud/HudParametrizedActionsPage.qml+2−2
@@ -19,7 +19,7 @@ import Ubuntu.Components 0.1
1919import "../Components"
2020
2121Item {
22− property alias header: header.text
22+ property alias header: header.title
2323 signal backPressed
2424 signal confirmPressed
2525 signal valuesUpdated
@@ -89,7 +89,7 @@ Item {
8989 anchors.left: parent.left
9090 anchors.right: parent.right
9191
92− PageHeaderLabel {
92+ PageHeader {
9393 id: header
9494 anchors.top: parent.top
9595 anchors.left: parent.left
qml/Panel/Panel.qml+1−31
@@ -24,13 +24,10 @@ Item {
2424 property real indicatorsMenuWidth: width
2525 property alias indicators: indicatorsMenu
2626 property bool fullscreenMode: false
27− property bool searchVisible: true
2827
2928 readonly property real separatorLineHeight: leftSeparatorLine.height
3029 readonly property real __panelMinusSeparatorLineHeight: panelHeight - separatorLineHeight
3130
32− signal searchClicked
33−
3431 function hideIndicatorMenu(delay) {
3532 if (delay !== undefined) {
3633 hideTimer.interval = delay;
@@ -104,7 +101,7 @@ Item {
104101 panelHeight: __panelMinusSeparatorLineHeight
105102 openedHeight: parent.height + (pinnedMode ? 0 : root.panelHeight)
106103 pinnedMode: !fullscreenMode
107− overFlowWidth: search.state=="hidden" ? parent.width : parent.width - search.width
104+ overFlowWidth: parent.width
108105 }
109106
110107 PanelSeparatorLine {
@@ -130,33 +127,6 @@ Item {
130127 source: "graphics/rectangular_dropshadow.sci"
131128 }
132129
133− SearchIndicator {
134− id: search
135− objectName: "search"
136− enabled: root.searchVisible
137−
138− state: {
139− if (parent.width < indicatorsMenu.width + width) {
140− if (indicatorsMenu.state != "initial") {
141− return "hidden";
142− }
143− }
144− if (root.searchVisible && !indicatorsMenu.showAll) {
145− return "visible";
146− }
147−
148− return "hidden";
149− }
150−
151− height: __panelMinusSeparatorLineHeight
152− anchors {
153− top: panelBackground.top
154− left: panelBackground.left
155− }
156−
157− onClicked: root.searchClicked()
158− }
159−
160130 states: [
161131 State {
162132 name: "in" //fully opaque and visible at top edge of screen
qml/ScopeTool.qml−1
@@ -73,7 +73,6 @@ Rectangle {
7373 id: dashContent
7474
7575 model: filteredScopes
76− searchHistory: SearchHistoryModel { }
7776 property var scope: scopes.getScope(currentIndex)
7877
7978 anchors {
qml/Shell.qml−1
@@ -493,7 +493,6 @@ FocusScope {
493493 property string focusedAppId: ApplicationManager.focusedApplicationId
494494 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
495495 fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
496− searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
497496
498497 InputFilterArea {
499498 anchors {
tests/autopilot/unity8/shell/emulators/dash.py+27−24
@@ -108,44 +108,47 @@ class Dash(emulators.UnityEmulatorBase):
108108 @autopilot_logging.log_action(logger.info)
109109 def _scroll_to_left_scope(self):
110110 original_index = self.dash_content_list.currentIndex
111− # Scroll on the border of the page header, because some scopes have
112− # contents that can be scrolled horizontally.
113− page_header = self._get_page_header()
114− border = page_header.select_single('QQuickBorderImage')
115− start_x = border.width / 3
116− stop_x = border.width / 3 * 2
117− start_y = stop_y = border.globalRect.y + border.height / 2
111+ dashContent = self.select_single(objectName="dashContent")
112+ start_x = dashContent.width / 3
113+ stop_x = dashContent.width / 3 * 2
114+ start_y = stop_y = dashContent.globalRect.y + 1
118115 self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
119116 self.dash_content_list.currentIndex.wait_for(original_index - 1)
120117
121− def _get_page_header(self):
122− return self.select_single('PageHeader', objectName='pageHeader')
123−
124118 @autopilot_logging.log_action(logger.info)
125119 def _scroll_to_right_scope(self):
126120 original_index = self.dash_content_list.currentIndex
127− # Scroll on the border of the page header, because some scopes have
128− # contents that can be scrolled horizontally.
129− page_header = self._get_page_header()
130− border = page_header.select_single('QQuickBorderImage')
131− start_x = border.width / 3 * 2
132− stop_x = border.width / 3
133− start_y = stop_y = border.globalRect.y + border.height / 2
121+ dashContent = self.select_single(objectName="dashContent")
122+ start_x = dashContent.width / 3 * 2
123+ stop_x = dashContent.width / 3
124+ start_y = stop_y = dashContent.globalRect.y + 1
134125 self.pointing_device.drag(start_x, start_y, stop_x, stop_y)
135126 self.dash_content_list.currentIndex.wait_for(original_index + 1)
136127
137128 def enter_search_query(self, query):
129+ current_header = self._get_current_page_header()
130+ self.pointing_device.move(current_header.globalRect.x +
131+ current_header.width - current_header.height / 2,
132+ current_header.globalRect.y +
133+ current_header.height / 2)
134+ self.pointing_device.click()
135+ headerContainer = current_header.select_single(objectName="headerContainer")
136+ headerContainer.contentY.wait_for(0)
138137 search_text_field = self._get_search_text_field()
139138 search_text_field.write(query)
140− search_text_field.state.wait_for('idle')
139+ current_header.select_single(objectName="searchIndicator").running.wait_for(False)
141140
142141 def _get_search_text_field(self):
143− page_header = self._get_page_header()
144− search_container = page_header.select_single(
145− 'QQuickItem', objectName='searchContainer')
146− search_container.state.wait_for(
147− MatchesAny(Equals('narrowActive'), Equals('active')))
148− return search_container.select_single(toolkit_emulators.TextField)
142+ page_header = self._get_current_page_header()
143+ return page_header.select_single(objectName='searchTextField')
144+
145+ def _get_current_page_header(self):
146+ dashContentList = self.select_single(objectName="dashContentList")
147+ all_headers = dashContentList.select_many("QQuickLoader")
148+ for i in all_headers:
149+ if i.isCurrent:
150+ return i.select_single(objectName="pageHeader")
151+ return None
149152
150153
151154class GenericScopeView(emulators.UnityEmulatorBase):
tests/autopilot/unity8/shell/emulators/main_window.py−5
@@ -133,9 +133,4 @@ class QQuickView(emulators.UnityEmulatorBase):
133133
134134 @autopilot_logging.log_action(logger.info)
135135 def search(self, query):
136− search_indicator = self._get_search_indicator()
137− self.pointing_device.click_object(search_indicator)
138136 self.get_dash().enter_search_query(query)
139−
140− def _get_search_indicator(self):
141− return self.select_single('SearchIndicator', objectName='search')
tests/autopilot/unity8/shell/tests/test_emulators.py−1
@@ -50,7 +50,6 @@ class MainWindowTestCase(tests.UnityTestCase):
5050 self.main_window.search('Test')
5151 text_field = self.main_window.get_dash()._get_search_text_field()
5252 self.assertEqual(text_field.text, 'Test')
53− self.assertEqual(text_field.state, 'idle')
5453
5554
5655class DashBaseTestCase(tests.UnityTestCase):
tests/qmltests/CMakeLists.txt+1−1
@@ -32,7 +32,7 @@ add_qml_test(Components ResponsiveGridView)
3232add_qml_test(Components ResponsiveVerticalJournal IMPORT_PATHS ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/plugins)
3333add_qml_test(Components SeeMore)
3434add_qml_test(Components Showable)
35−add_qml_test(Components PageHeaderLabel)
35+add_qml_test(Components PageHeader)
3636add_qml_test(Components ZoomableImage)
3737add_qml_test(Dash Dash)
3838add_qml_test(Dash DashContent)
tests/qmltests/Components/tst_PageHeaderLabel.qml → tests/qmltests/Components/tst_PageHeader.qmlrenamed+87−54
@@ -23,13 +23,10 @@ import Unity 0.2
2323import Unity.Test 0.1 as UT
2424
2525Item {
26+ id: root
2627 width: units.gu(110)
2728 height: units.gu(30)
2829
29− MockScope {
30− id: scopeMock
31− }
32−
3330 UT.UnityTestCase {
3431 name: "PageHeaderLabelTest"
3532 when: windowShown
@@ -82,33 +79,6 @@ Item {
8279 compare(searchQuery, "", "Reset search did not reset searchQuery correctly.")
8380 }
8481
85− function test_move_search_by_width()
86− {
87− searchEnabled = true
88− pageHeader.resetSearch()
89−
90− var searchContainer = findChild(pageHeader, "searchContainer")
91−
92− parent.width = units.gu(40)
93−
94− verify(searchContainer !== undefined)
95− verify(searchContainer.y <= 0)
96−
97− pageHeader.triggerSearch()
98− verify(searchContainer.y >= 0)
99−
100− pageHeader.resetSearch()
101− verify(searchContainer.y <= 0)
102−
103− parent.width = units.gu(110)
104− verify(searchContainer.y >= 0)
105−
106− pageHeader.triggerSearch()
107− tryCompare(searchContainer, "narrowMode", false)
108− tryCompare(searchContainer, "state", "active")
109− tryCompare(searchContainer, "width", units.gu(40))
110− }
111−
11282 function test_history() {
11383 pageHeader.searchHistory.clear()
11484 compare(pageHeader.searchHistory.count, 0)
@@ -144,21 +114,41 @@ Item {
144114
145115 function test_search_indicator() {
146116 var searchIndicator = findChild(pageHeader, "searchIndicator")
147− var primaryImage = findChild(pageHeader, "primaryImage")
117+ var clearIcon = findChild(pageHeader, "clearIcon")
148118
149119 pageHeader.triggerSearch()
150120
151− scopeMock.setSearchInProgress(false);
121+ tryCompare(clearIcon, "visible", false)
122+ pageHeader.searchQuery = "ubuntu"
123+ tryCompare(clearIcon, "visible", true)
124+
125+ pageHeader.searchInProgress = false
152126 compare(searchIndicator.running, false, "Search indicator is running.")
153− tryCompare(primaryImage, "visible", true)
127+ tryCompare(clearIcon, "visible", true)
154128
155− scopeMock.setSearchInProgress(true);
129+ pageHeader.searchInProgress = true
156130 compare(searchIndicator.running, true, "Search indicator isn't running.")
157− tryCompare(primaryImage, "visible", false)
131+ tryCompare(clearIcon, "visible", false)
132+
133+ pageHeader.searchInProgress = false;
134+ compare(searchIndicator.running, false, "Search indicator is running.")
135+ tryCompare(clearIcon, "visible", true)
136+ }
137+
138+ function test_titleImage() {
139+
140+ var titleImage = findChild(pageHeader, "titleImage");
141+ verify(titleImage == null);
142+
143+ showImageCheckBox.checked = true;
144+
145+ titleImage = findChild(pageHeader, "titleImage");
146+ verify(titleImage !== null);
147+ compare(titleImage.source, pageHeader.titleImageSource);
148+
158149 }
159150
160151 function cleanup() {
161− scopeMock.setSearchInProgress(false);
162152 pageHeader.resetSearch();
163153 }
164154
@@ -171,14 +161,21 @@ Item {
171161
172162 pageHeader.triggerSearch();
173163
174− var searchContainer = findChild(pageHeader, "searchContainer");
175− verify(searchContainer !== undefined, "searchContainer != undefined");
176− tryCompareFunction(function() { return searchContainer.popover !== null; }, true);
164+ var headerContainer = findChild(pageHeader, "headerContainer");
165+ verify(headerContainer !== null, "headerContainer != null");
166+ tryCompareFunction(function() { return headerContainer.popover !== null; }, true);
177167
178− tryCompare(searchContainer.popover, "visible", true);
168+ tryCompare(headerContainer.popover, "visible", true);
179169 }
180170
181− function test_resetSearch_onPopupClose() {
171+ function test_tap_outside_closes_popup_data() {
172+ return [
173+ { tag: "with search text", searchText: "foobar", hideSearch: false },
174+ { tag: "without search text", searchText: "", hideSearch: true }
175+ ];
176+ }
177+
178+ function test_tap_outside_closes_popup(data) {
182179 searchEnabled = true;
183180 pageHeader.searchHistory.clear();
184181
@@ -187,16 +184,19 @@ Item {
187184
188185 pageHeader.triggerSearch();
189186
190− var searchContainer = findChild(pageHeader, "searchContainer");
191− verify(searchContainer !== undefined, "searchContainer != undefined");
192− tryCompareFunction(function() { return searchContainer.popover !== null; }, true);
193− compare(searchContainer.popover.visible, true);
187+ var headerContainer = findChild(pageHeader, "headerContainer");
188+ verify(headerContainer !== null, "headerContainer != null");
189+ tryCompareFunction(function() { return headerContainer.popover !== null; }, true);
190+ compare(headerContainer.popover.visible, true);
191+
192+ pageHeader.searchQuery = data.searchText;
194193
195− pageHeader.searchQuery = "test";
196− tryCompareFunction( function() { return (searchContainer.popover===null || !searchContainer.popover.visible) }, true);
194+ mouseClick(root, root.width / 2, root.height - 1);
195+
196+ tryCompare(headerContainer, "showSearch", !data.hideSearch);
197+ tryCompareFunction(function() { return headerContainer.popover === null; }, true);
197198
198199 pageHeader.resetSearch();
199− compare((searchContainer.popover===null || !searchContainer.popover.visible), true);
200200 }
201201 }
202202
@@ -204,18 +204,21 @@ Item {
204204 anchors.fill: parent
205205 spacing: units.gu(1)
206206
207− PageHeaderLabel {
207+ PageHeader {
208208 id: pageHeader
209209 anchors {
210210 left: parent.left
211211 right: parent.right
212212 }
213213
214− scope: scopeMock
215−
216214 searchEntryEnabled: true
217− searchHistory: SearchHistoryModel {}
218− text: "%^$%^%^&%^&%^$%GHR%"
215+ title: "%^$%^%^&%^&%^$%GHR%"
216+ imageSource: showImageCheckBox.checked ? titleImageSource : ""
217+ showBackButton: showBackButtonCheckBox.checked
218+
219+ property string titleImageSource: Qt.resolvedUrl("tst_PageHeader/logo-ubuntu-orange.svg")
220+ property date lastBackClicked
221+ onBackClicked: lastBackClicked = new Date()
219222 }
220223
221224 Row {
@@ -251,5 +254,35 @@ Item {
251254 anchors.verticalCenter: parent.verticalCenter
252255 }
253256 }
257+
258+ Row {
259+ spacing: units.gu(1)
260+ anchors {
261+ left: parent.left
262+ right: parent.right
263+ }
264+ CheckBox {
265+ id: showBackButtonCheckBox
266+ }
267+ Label {
268+ text: "Back button enabled (Last clicked: " + Qt.formatTime(pageHeader.lastBackClicked, "hh:mm:ss") + ")"
269+ anchors.verticalCenter: parent.verticalCenter
270+ }
271+ }
272+
273+ Row {
274+ spacing: units.gu(1)
275+ anchors {
276+ left: parent.left
277+ right: parent.right
278+ }
279+ CheckBox {
280+ id: showImageCheckBox
281+ }
282+ Label {
283+ text: "Show image instead of title"
284+ anchors.verticalCenter: parent.verticalCenter
285+ }
286+ }
254287 }
255288}
tests/qmltests/Components/tst_PageHeader/logo-ubuntu-orange.svgnew file+60
@@ -0,0 +1,60 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!-- Generator: Adobe Illustrator 17.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5+ width="107px" height="25px" viewBox="0 0 107 25" enable-background="new 0 0 107 25" xml:space="preserve">
6+<g id="BK">
7+</g>
8+<g id="Comp">
9+ <circle fill="#DD4814" cx="100.596" cy="7.374" r="6.403"/>
10+ <path fill="#FFFFFF" d="M96.6,6.605c-0.424,0-0.768,0.344-0.768,0.77c0,0.423,0.344,0.768,0.768,0.768
11+ c0.425,0,0.772-0.345,0.772-0.768C97.371,6.949,97.025,6.605,96.6,6.605z M102.094,10.103c-0.365,0.211-0.492,0.682-0.282,1.047
12+ c0.214,0.372,0.685,0.497,1.051,0.284c0.369-0.21,0.496-0.682,0.281-1.05C102.932,10.014,102.462,9.889,102.094,10.103z
13+ M98.272,7.375c0-0.76,0.378-1.434,0.956-1.839l-0.562-0.944c-0.675,0.451-1.176,1.138-1.384,1.943
14+ c0.246,0.2,0.398,0.501,0.398,0.84c0,0.337-0.152,0.639-0.398,0.838c0.208,0.805,0.709,1.492,1.384,1.941l0.562-0.941
15+ C98.65,8.807,98.272,8.134,98.272,7.375z M100.518,5.125c1.177,0,2.14,0.902,2.24,2.052l1.098-0.019
16+ c-0.055-0.846-0.425-1.61-0.994-2.166c-0.295,0.108-0.634,0.093-0.925-0.078c-0.292-0.168-0.473-0.452-0.524-0.761
17+ c-0.285-0.077-0.584-0.12-0.895-0.12c-0.53,0-1.032,0.125-1.48,0.345l0.535,0.958C99.86,5.201,100.182,5.125,100.518,5.125z
18+ M100.518,9.622c-0.336,0-0.658-0.076-0.945-0.21l-0.535,0.958c0.447,0.221,0.95,0.344,1.48,0.344c0.312,0,0.61-0.039,0.895-0.119
19+ c0.051-0.311,0.232-0.596,0.524-0.763c0.294-0.168,0.63-0.186,0.925-0.073c0.569-0.562,0.938-1.323,0.994-2.17l-1.098-0.015
20+ C102.658,8.721,101.695,9.622,100.518,9.622z M102.094,4.646c0.368,0.213,0.838,0.088,1.051-0.282
21+ c0.214-0.367,0.087-0.839-0.281-1.052c-0.366-0.211-0.837-0.085-1.051,0.283C101.601,3.962,101.728,4.433,102.094,4.646z"/>
22+ <path fill="#DD4814" d="M12.807,24.177c-0.649,0.162-1.509,0.337-2.577,0.518c-1.068,0.184-2.304,0.276-3.704,0.276
23+ c-1.222,0-2.247-0.179-3.082-0.535c-0.833-0.355-1.503-0.858-2.01-1.509c-0.509-0.65-0.875-1.419-1.098-2.302
24+ C0.11,19.739,0,18.76,0,17.68V8.778h2.835v8.293c0,1.932,0.304,3.315,0.916,4.147c0.61,0.834,1.636,1.249,3.079,1.249
25+ c0.304,0,0.62-0.01,0.945-0.031c0.326-0.02,0.632-0.045,0.914-0.075C8.974,22.33,9.234,22.3,9.468,22.27
26+ c0.234-0.032,0.401-0.065,0.504-0.108V8.778h2.835V24.177L12.807,24.177z"/>
27+ <path fill="#DD4814" d="M19.728,9.478c0.347-0.222,0.871-0.458,1.572-0.7c0.7-0.243,1.507-0.367,2.424-0.367
28+ c1.137,0,2.148,0.205,3.033,0.609c0.886,0.406,1.632,0.977,2.243,1.709c0.607,0.731,1.069,1.605,1.385,2.622
29+ c0.317,1.016,0.474,2.135,0.474,3.355c0,1.279-0.187,2.433-0.565,3.461c-0.377,1.025-0.909,1.894-1.6,2.605
30+ c-0.692,0.714-1.524,1.26-2.502,1.647C25.219,24.807,24.121,25,22.899,25c-1.321,0-2.49-0.093-3.506-0.276
31+ c-1.016-0.183-1.849-0.367-2.501-0.549V1.46l2.837-0.488L19.728,9.478L19.728,9.478z M19.728,22.162
32+ c0.285,0.084,0.686,0.159,1.206,0.23c0.517,0.073,1.161,0.107,1.935,0.107c1.523,0,2.746-0.504,3.659-1.509
33+ c0.915-1.007,1.373-2.435,1.373-4.284c0-0.813-0.081-1.575-0.245-2.288c-0.163-0.712-0.428-1.326-0.793-1.845
34+ c-0.366-0.519-0.839-0.923-1.416-1.219c-0.582-0.297-1.278-0.444-2.09-0.444c-0.774,0-1.484,0.133-2.135,0.395
35+ c-0.651,0.267-1.149,0.541-1.495,0.825v10.032H19.728z"/>
36+ <path fill="#DD4814" d="M46.684,24.177c-0.651,0.162-1.509,0.337-2.576,0.518c-1.067,0.184-2.303,0.276-3.707,0.276
37+ c-1.219,0-2.246-0.179-3.079-0.535c-0.833-0.355-1.504-0.858-2.011-1.509c-0.511-0.65-0.875-1.419-1.098-2.302
38+ c-0.224-0.886-0.335-1.865-0.335-2.944V8.778h2.835v8.293c0,1.932,0.304,3.315,0.914,4.147c0.611,0.834,1.636,1.249,3.08,1.249
39+ c0.305,0,0.622-0.01,0.947-0.031c0.325-0.02,0.629-0.045,0.915-0.075c0.284-0.032,0.544-0.062,0.779-0.092
40+ c0.232-0.032,0.4-0.065,0.503-0.108V8.778h2.835v15.399H46.684z"/>
41+ <path fill="#DD4814" d="M50.769,9.236c0.651-0.164,1.514-0.335,2.593-0.521c1.077-0.18,2.316-0.272,3.719-0.272
42+ c1.263,0,2.308,0.178,3.142,0.533c0.834,0.356,1.499,0.853,1.997,1.494c0.498,0.64,0.849,1.408,1.053,2.3
43+ c0.202,0.898,0.304,1.882,0.304,2.959v8.905h-2.836V16.34c0-0.977-0.067-1.809-0.198-2.501c-0.132-0.693-0.351-1.249-0.656-1.676
44+ c-0.303-0.429-0.712-0.738-1.221-0.93c-0.506-0.197-1.139-0.292-1.888-0.292c-0.306,0-0.622,0.012-0.944,0.032
45+ c-0.326,0.021-0.637,0.046-0.931,0.076c-0.298,0.03-0.56,0.067-0.794,0.107c-0.235,0.041-0.402,0.072-0.503,0.091v13.386h-2.837
46+ L50.769,9.236L50.769,9.236z"/>
47+ <path fill="#DD4814" d="M70.132,8.778h6.008v2.378h-6.008v7.319c0,0.792,0.062,1.45,0.185,1.966
48+ c0.121,0.52,0.304,0.927,0.548,1.221c0.244,0.292,0.549,0.501,0.914,0.625c0.367,0.122,0.794,0.182,1.28,0.182
49+ c0.874,0,1.565-0.096,2.076-0.29c0.507-0.193,0.853-0.329,1.035-0.412l0.609,2.32c-0.283,0.142-0.787,0.324-1.508,0.547
50+ c-0.722,0.225-1.539,0.338-2.454,0.338c-1.078,0-1.968-0.137-2.669-0.413c-0.701-0.275-1.265-0.685-1.692-1.234
51+ c-0.427-0.548-0.727-1.224-0.898-2.027c-0.175-0.805-0.26-1.734-0.26-2.79V4.356l2.833-0.489v4.911H70.132z"/>
52+ <path fill="#DD4814" d="M91.57,24.177c-0.653,0.162-1.51,0.337-2.579,0.518c-1.067,0.184-2.3,0.276-3.701,0.276
53+ c-1.222,0-2.247-0.179-3.081-0.535c-0.836-0.355-1.506-0.858-2.013-1.509c-0.509-0.65-0.873-1.419-1.1-2.302
54+ c-0.221-0.886-0.335-1.865-0.335-2.944V8.778h2.838v8.293c0,1.932,0.305,3.315,0.914,4.147c0.609,0.834,1.637,1.249,3.081,1.249
55+ c0.304,0,0.618-0.01,0.945-0.031c0.323-0.02,0.629-0.045,0.914-0.075c0.284-0.032,0.546-0.062,0.777-0.092
56+ c0.234-0.032,0.403-0.065,0.505-0.108V8.778h2.837v15.399H91.57z"/>
57+</g>
58+<g id="Grid" display="none">
59+</g>
60+</svg>
tests/qmltests/Dash/tst_Dash.qml−39
@@ -70,24 +70,6 @@ Item {
7070 ]
7171 }
7272
73− function test_set_current_scope_data() {
74− return get_scope_data()
75− }
76−
77− function test_set_current_scope(data) {
78− // wait for scopes to load
79− tryCompare(scopes, "loaded", true);
80−
81− var tabbar = findChild(dash, "tabbar");
82− verify(tabbar != undefined)
83− var dashContent = findChild(dash, "dashContent");
84− var current_index = dashContent.currentIndex;
85−
86− dash.setCurrentScope(data.tag, true /* animate */, false /* reset */);
87− compare(dashContent.currentIndex, data.shouldBeVisible ? data.visualIndex : current_index);
88− compare(tabbar.selectedIndex, data.shouldBeVisible ? data.visualIndex : current_index);
89− }
90−
9173 function test_show_scope_on_load_data() {
9274 return get_scope_data()
9375 }
@@ -109,26 +91,5 @@ Item {
10991 verify(dashContentList != undefined);
11092 tryCompare(dashContentList, "currentIndex", data.visualIndex);
11193 }
112−
113− function test_dash_bar_set_index_connection_data() {
114− return get_scope_data()
115− }
116−
117− function test_dash_bar_set_index_connection(data) {
118− if (data.shouldBeVisible == false) {
119− console.log("Not testing " + data.tag + ": not visible");
120− return;
121− }
122− // wait for scopes to load
123− tryCompare(scopes, "loaded", true);
124−
125− var tabbar = findChild(dash, "tabbar");
126− verify(tabbar != undefined)
127− var dashContent = findChild(dash, "dashContent");
128− var current_index = dashContent.currentIndex;
129−
130− tabbar.model.selectedIndex = data.visualIndex;
131− compare(dashContent.currentIndex, data.shouldBeVisible ? data.visualIndex : current_index);
132− }
13394 }
13495}
tests/qmltests/Dash/tst_DashContent.qml+5−67
@@ -47,8 +47,6 @@ Item {
4747 model: scopesModel
4848 }
4949 scopes : scopesModel
50−
51− searchHistory: SearchHistoryModel {}
5250 }
5351
5452 SignalSpy {
@@ -194,8 +192,8 @@ Item {
194192 function test_scope_mapping(data) {
195193 dashContent.setCurrentScopeAtIndex(data.index, true, false);
196194 tryCompareFunction(get_current_item_object_name, data.objectName)
197− var pageHeader = findChild(dashContent, "pageHeader");
198− compare(pageHeader.scope, scopesModel.getScope(data.index));
195+ var pageHeader = findChild(dashContent, "innerPageHeader");
196+ compare(pageHeader.title, scopesModel.getScope(data.index).name);
199197 }
200198
201199 function test_is_active_data() {
@@ -217,67 +215,6 @@ Item {
217215 tryCompare(scopesModel.getScope(2), "isActive", data.active2);
218216 }
219217
220− function doFindMusicButton(parent) {
221− for (var i = 0; i < parent.children.length; i++) {
222− var c = parent.children[i];
223− if (UT.Util.isInstanceOf(c, "AbstractButton") && parent.x >= 0) {
224− for (var ii = 0; ii < c.children.length; ii++) {
225− var cc = c.children[ii];
226− if (UT.Util.isInstanceOf(cc, "Label") && cc.text == "Music") {
227− return c;
228− }
229− }
230− }
231− var r = doFindMusicButton(c);
232− if (r !== undefined) {
233− return r;
234− }
235− }
236− return undefined;
237− }
238−
239− function findMusicButton() {
240− // We need to find a AbstractButton that has a Label child
241− // with text Music and it's parent x is >= 0
242− var tabbar = findChild(dashContent, "tabbar");
243− return doFindMusicButton(tabbar);
244− }
245−
246− function test_tabBar_index_change() {
247− tryCompare(scopesModel, "loaded", true);
248− var tabbar = findChild(dashContent, "tabbar");
249−
250− tryCompare(dashContent, "currentIndex", 0);
251− tryCompare(tabbar, "selectedIndex", 0);
252− tryCompare(tabbar, "selectionMode", false);
253−
254− mouseClick(tabbar, units.gu(5), units.gu(5))
255−
256− tryCompare(tabbar, "selectionMode", true);
257− tryCompare(tabbar, "selectedIndex", 0);
258− tryCompare(dashContent, "currentIndex", 0);
259−
260− var button;
261− tryCompareFunction(function() { button = findMusicButton(); return button != undefined; }, true);
262− waitForRendering(button);
263−
264− tryCompareFunction(function() { return button.opacity > 0; }, true);
265− mouseClick(button, button.width / 2, button.height / 2)
266−
267− tryCompare(tabbar, "selectionMode", false);
268− tryCompare(tabbar, "selectedIndex", 1);
269− tryCompare(dashContent, "currentIndex", 1);
270− }
271−
272− function test_tabBar_listens_to_index_change() {
273− var tabbar = findChild(dashContent, "tabbar");
274− tryCompare(dashContent, "currentIndex", 0);
275− compare(tabbar.selectedIndex, 0);
276− var dashContentList = findChild(dashContent, "dashContentList");
277− dashContentList.currentIndex = 1;
278− compare(tabbar.selectedIndex, 1);
279− }
280−
281218 function checkFlickMovingAndNotInteractive()
282219 {
283220 var dashContentList = findChild(dashContent, "dashContentList");
@@ -318,8 +255,8 @@ Item {
318255 }
319256
320257 function closePreview() {
321− var closePreviewMouseArea = findChild(dashContent, "dashContentPreviewList_pageHeader_backButton");
322− mouseClick(closePreviewMouseArea, closePreviewMouseArea.width / 2, closePreviewMouseArea.height / 2);
258+ var closePreviewMouseArea = findChild(dashContent, "innerPageHeader");
259+ mouseClick(closePreviewMouseArea, units.gu(2), units.gu(2));
323260
324261 var previewListView = findChild(dashContent, "dashContentPreviewList");
325262 tryCompare(previewListView, "open", false);
@@ -461,6 +398,7 @@ Item {
461398 compare(allButton.visible, true);
462399 compare(backButton.visible, true);
463400
401+ waitForRendering(departmentListView);
464402 mouseClick(allButton, 0, 0);
465403 compare(dashDepartments.showList, false);
466404 tryCompare(dashDepartments.currentDepartment, "departmentId", "middle2");
tests/qmltests/Dash/tst_GenericScopeView.qml−26
@@ -48,19 +48,11 @@ Item {
4848 signal mainStageFocusedApplicationChanged()
4949 }
5050
51− PageHeaderLabel {
52− id: testPageHeader
53− searchHistory: SearchHistoryModel {}
54− text: genericScopeView.scope ? genericScopeView.scope.name : ""
55− width: parent.width
56− }
5751
5852 GenericScopeView {
5953 id: genericScopeView
6054 anchors.fill: parent
6155 previewListView: previewListView
62− pageHeader: testPageHeader
63− tabBarHeight: testPageHeader.implicitHeight
6456
6557 UT.UnityTestCase {
6658 name: "GenericScopeView"
@@ -73,15 +65,6 @@ Item {
7365 tryCompare(genericScopeView.categoryView, "contentY", 0)
7466 }
7567
76− function test_isCurrent() {
77− genericScopeView.isCurrent = true
78− testPageHeader.searchQuery = "test"
79− previewListView.open = true
80− genericScopeView.isCurrent = false
81− tryCompare(testPageHeader, "searchQuery", "")
82− tryCompare(previewListView, "open", false);
83− }
84−
8568 function test_isActive() {
8669 tryCompare(genericScopeView.scope, "isActive", false)
8770 genericScopeView.isCurrent = true
@@ -170,14 +153,6 @@ Item {
170153
171154 categoryListView.positionAtBeginning();
172155
173− // wait for the header0 to be on its position
174− tryCompareFunction(
175− function() {
176− var header0 = findChild(genericScopeView, "dashSectionHeader0")
177− return header0.y == testPageHeader.height;
178− },
179− true);
180−
181156 var header0 = findChild(genericScopeView, "dashSectionHeader0")
182157 var category0 = findChild(genericScopeView, "dashCategory0")
183158 mouseClick(header0, header0.width / 2, header0.height / 2);
@@ -211,7 +186,6 @@ Item {
211186 id: previewListView
212187 anchors.fill: parent
213188 visible: false
214− pageHeader: testPageHeader
215189 scope: genericScopeView.scope
216190 }
217191}
tests/qmltests/Panel/tst_Panel.qml−35
@@ -30,13 +30,6 @@ Item {
3030 width: units.gu(40)
3131 height: units.gu(80)
3232
33− property bool searchClicked: false
34−
35− Connections {
36− target: panel
37− onSearchClicked: searchClicked = true
38− }
39−
4033 Panel {
4134 id: panel
4235 anchors.fill: parent
@@ -62,7 +55,6 @@ Item {
6255 function init() {
6356 panel.indicators.initialise();
6457
65− searchClicked = false;
6658 panel.indicators.hide();
6759 tryCompare(panel.indicators.hideAnimation, "running", false);
6860 tryCompare(panel.indicators, "state", "initial");
@@ -205,38 +197,11 @@ Item {
205197 }
206198 }
207199
208− function test_search_click_when_visible() {
209− panel.fullscreenMode = false;
210− panel.searchVisible = true;
211−
212− var searchIndicator = findChild(panel, "search");
213− verify(searchIndicator !== null);
214−
215− tap(searchIndicator, 1, 1);
216−
217− compare(searchClicked, true,
218− "Tapping search indicator while it was enabled did not emit searchClicked signal");
219− }
220−
221− function test_search_click_when_not_visible() {
222− panel.fullscreenMode = false;
223− panel.searchVisible = false;
224−
225− var searchIndicator = findChild(panel, "search");
226− verify(searchIndicator !== null);
227−
228− tap(searchIndicator, 1, 1);
229−
230− compare(searchClicked, false,
231− "Tapping search indicator while it was not visible emitted searchClicked signal");
232− }
233−
234200 // Test the vertical velocity check when flicking the indicators open at an angle.
235201 // If the vertical velocity is above a specific point, we shouldnt change active indicators
236202 // if the x position changes
237203 function test_vertical_velocity_detector() {
238204 panel.fullscreenMode = false;
239− panel.searchVisible = false;
240205
241206 var indicatorRow = findChild(panel.indicators, "indicatorRow");
242207 verify(indicatorRow !== null);
tests/qmltests/tst_Shell.qml−36
@@ -510,42 +510,6 @@ Item {
510510 tryCompare(dash, "shown", data.expectedShown);
511511 }
512512
513− function test_searchIndicatorHidesOnAppFocus() {
514− var searchIndicator = findChild(shell, "container")
515− tryCompare(searchIndicator, "opacity", 1)
516− dragLauncherIntoView();
517−
518− // Launch an app from the launcher
519− tapOnAppIconInLauncher();
520− waitUntilApplicationWindowIsFullyVisible();
521−
522− tryCompare(searchIndicator, "opacity", 0);
523− }
524−
525− function test_searchIndicatorHidesOnGreeterShown() {
526− var searchIndicator = findChild(shell, "container")
527− var greeter = findChild(shell, "greeter");
528−
529− tryCompare(searchIndicator, "opacity", 1)
530−
531− greeter.show()
532− tryCompare(greeter, "shown", true)
533− tryCompare(searchIndicator, "opacity", 0)
534− }
535−
536− function test_searchIndicatorHideOnPreviewShown() {
537− var searchIndicator = findChild(shell, "container");
538− var dashContent = findChild(shell, "dashContent");
539−
540− verify(dashContent != null);
541−
542− tryCompare(searchIndicator, "opacity", 1);
543−
544− dashContent.previewOpen = true;
545−
546− tryCompare(searchIndicator, "opacity", 0);
547− }
548−
549513 function test_focusRequestedHidesGreeter() {
550514 var greeter = findChild(shell, "greeter")
551515