@@ -19,18 +19,18 @@ import Ubuntu.Components 1.1
19import Ubuntu.Components.Themes.Ambiance 1.1
19import Ubuntu.Components.Themes.Ambiance 1.1
20import Ubuntu.Components.Popups 1.0
20import Ubuntu.Components.Popups 1.0
21import Ubuntu.Components.ListItems 1.0
21import Ubuntu.Components.ListItems 1.0
22+import "SearchHistoryModel"
25 implicitHeight: headerContainer.height + units.gu(2) + bottomContainer.height
26 implicitHeight: headerContainer.height + units.gu(2) + bottomContainer.height
26− height: implicitHeight
28 property bool showBackButton: false
28 property bool showBackButton: false
29 property string title
29 property string title
30 property string imageSource
30 property string imageSource
32 property bool searchEntryEnabled: false
32 property bool searchEntryEnabled: false
33− property ListModel searchHistory
33+ property ListModel searchHistory: SearchHistoryModel
34 property alias searchQuery: searchTextField.text
34 property alias searchQuery: searchTextField.text
35 property bool searchInProgress: false
35 property bool searchInProgress: false
@@ -43,18 +43,14 @@ Item {
43 signal backClicked()
43 signal backClicked()
45 function triggerSearch() {
45 function triggerSearch() {
46− if (searchEntryEnabled) searchTextField.forceActiveFocus()
46+ if (searchEntryEnabled) searchTextField.forceActiveFocus();
49− function resetSearch(unfocusField) {
49+ function resetSearch(keepFocus) {
50− if (unfocusField == undefined) {
54 if (searchHistory) {
50 if (searchHistory) {
55 searchHistory.addQuery(searchTextField.text);
51 searchHistory.addQuery(searchTextField.text);
57− if (unfocusField) {
53+ if (!keepFocus) {
58 unfocus();
54 unfocus();
60 searchTextField.text = "";
56 searchTextField.text = "";
@@ -65,27 +61,38 @@ Item {
66 function unfocus() {
62 function unfocus() {
67 searchTextField.focus = false;
63 searchTextField.focus = false;
64+ if (!searchTextField.text) {
65+ headerContainer.showSearch = false;
70 function openSearchHistory() {
69 function openSearchHistory() {
71 if (openSearchAnimation.running) {
70 if (openSearchAnimation.running) {
72− openSearchAnimation.openSearchHistory = true
71+ openSearchAnimation.openSearchHistory = true;
73 } else if (root.searchHistory.count > 0 && headerContainer.popover == null) {
72 } else if (root.searchHistory.count > 0 && headerContainer.popover == null) {
74 headerContainer.popover = PopupUtils.open(popoverComponent, searchTextField,
73 headerContainer.popover = PopupUtils.open(popoverComponent, searchTextField,
76− "contentWidth": searchTextField.width,
75+ "contentWidth": searchTextField.width,
77− "edgeMargins": units.gu(1)
76+ "edgeMargins": units.gu(1)
83 onImageSourceChanged: {
82 onImageSourceChanged: {
84− if (imageSource.length > 0) {
83+ if (imageSource) {
85 header.contents = imageComponent.createObject();
84 header.contents = imageComponent.createObject();
87 header.contents.destroy();
86 header.contents.destroy();
88− header.contents = null
87+ header.contents = null;
93+ visible: headerContainer.popover !== null
95+ PopupUtils.close(headerContainer.popover);
@@ -110,7 +117,7 @@ Item {
110 onRunningChanged: {
117 onRunningChanged: {
111 if (!running && openSearchAnimation.openSearchHistory) {
118 if (!running && openSearchAnimation.openSearchHistory) {
112 openSearchAnimation.openSearchHistory = false;
119 openSearchAnimation.openSearchHistory = false;
113− root.openSearchHistory()
120+ root.openSearchHistory();
@@ -118,8 +125,7 @@ Item {
120 id: headersColumn
127 id: headersColumn
121− anchors.fill: parent
128+ anchors { left: parent.left; right: parent.right }
122− height: searchHeader.height + header.height
124 PageHeadStyle {
130 PageHeadStyle {
125 id: searchHeader
131 id: searchHeader
@@ -127,14 +133,15 @@ Item {
127 height: headerContainer.height
133 height: headerContainer.height
128 contentHeight: height
134 contentHeight: height
129 separatorSource: ""
135 separatorSource: ""
136+ // Required to keep PageHeadStyle noise down as it expects the Page's properties around.
130 property var styledItem: searchHeader
137 property var styledItem: searchHeader
131− property string title: "searchHeader"
138+ property string title
132 property var config: PageHeadConfiguration {
139 property var config: PageHeadConfiguration {
133 backAction: Action {
140 backAction: Action {
134 iconName: "back"
141 iconName: "back"
135 onTriggered: {
142 onTriggered: {
136− root.resetSearch(true)
143+ root.resetSearch();
137− headerContainer.showSearch = false
144+ headerContainer.showSearch = false;
@@ -142,7 +149,7 @@ Item {
142 id: searchTextField
149 id: searchTextField
143 hasClearButton: false
150 hasClearButton: false
144 anchors {
151 anchors {
145− fill: parent;
152+ fill: parent
146 leftMargin: units.gu(1)
153 leftMargin: units.gu(1)
147 topMargin: units.gu(1)
154 topMargin: units.gu(1)
148 bottomMargin: units.gu(1)
155 bottomMargin: units.gu(1)
@@ -160,6 +167,9 @@ Item {
160 source: "image://theme/clear"
167 source: "image://theme/clear"
161 opacity: searchTextField.text.length > 0 && !searchActivityIndicator.running
168 opacity: searchTextField.text.length > 0 && !searchActivityIndicator.running
162 visible: opacity > 0
169 visible: opacity > 0
170+ Behavior on opacity {
171+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
165 ActivityIndicator {
175 ActivityIndicator {
@@ -175,14 +185,14 @@ Item {
177 onClicked: {
187 onClicked: {
178− root.resetSearch(false)
188+ root.resetSearch(true);
179− root.openSearchHistory()
189+ root.openSearchHistory();
183 onActiveFocusChanged: {
193 onActiveFocusChanged: {
184 if (activeFocus) {
194 if (activeFocus) {
185− root.openSearchHistory()
195+ root.openSearchHistory();
@@ -202,7 +212,7 @@ Item {
202 iconName: "back"
212 iconName: "back"
203 visible: root.showBackButton
213 visible: root.showBackButton
204 onTriggered: {
214 onTriggered: {
205− root.backClicked()
215+ root.backClicked();
@@ -211,8 +221,8 @@ Item {
211 iconName: "search"
221 iconName: "search"
212 visible: root.searchEntryEnabled
222 visible: root.searchEntryEnabled
213 onTriggered: {
223 onTriggered: {
214− headerContainer.showSearch = true
224+ headerContainer.showSearch = true;
215− searchTextField.forceActiveFocus()
225+ searchTextField.forceActiveFocus();
@@ -220,7 +230,7 @@ Item {
221 property var contents: null
231 property var contents: null
222 Component.onCompleted: {
232 Component.onCompleted: {
223− if (root.imageSource.length > 0) {
233+ if (root.imageSource) {
224 header.contents = imageComponent.createObject();
234 header.contents = imageComponent.createObject();
@@ -236,6 +246,7 @@ Item {
236 source: root.imageSource
246 source: root.imageSource
237 fillMode: Image.PreserveAspectFit
247 fillMode: Image.PreserveAspectFit
238 horizontalAlignment: Image.AlignLeft
248 horizontalAlignment: Image.AlignLeft
249+ sourceSize.height: height
@@ -247,29 +258,10 @@ Item {
247 id: popoverComponent
258 id: popoverComponent
248 Popover {
259 Popover {
249 id: popover
260 id: popover
251 Component.onDestruction: {
263 Component.onDestruction: {
252− headerContainer.popover = null
264+ headerContainer.popover = null;
255− // FIXME: this should go into the first item below, but enable: false
256− // prevents mouse events propagation
265− onClicked: PopupUtils.close(popover)
268− anchors.centerIn: parent
271− source: "../Dash/graphics/icon_listview_clear.png"
@@ -289,9 +281,9 @@ Item {
289 showDivider: index < recentSearches.count - 1
281 showDivider: index < recentSearches.count - 1
290 text: query
282 text: query
291 onClicked: {
283 onClicked: {
292− searchHistory.addQuery(text)
284+ searchHistory.addQuery(text);
293− searchTextField.text = text
285+ searchTextField.text = text;
294− PopupUtils.close(popover)
286+ PopupUtils.close(popover);