2 * Copyright (C) 2013 Canonical, Ltd.
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.
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.
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/>.
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 "../Components"
23 import "../Components/SearchHistoryModel"
27 objectName: "pageHeader"
28 implicitHeight: headerContainer.height + bottomContainer.height + (showSignatureLine ? units.gu(2) : 0)
30 property bool showBackButton: false
33 property bool searchEntryEnabled: false
34 property bool settingsEnabled: false
35 property bool favoriteEnabled: false
36 property bool favorite: false
37 property ListModel searchHistory: SearchHistoryModel
38 property alias searchQuery: searchTextField.text
39 property alias searchHint: searchTextField.placeholderText
40 property bool showSignatureLine: true
42 property alias bottomItem: bottomContainer.children
43 property int paginationCount: 0
44 property int paginationIndex: -1
46 property var scopeStyle: null
49 signal settingsClicked()
50 signal favoriteClicked()
52 onScopeStyleChanged: refreshLogo()
53 onSearchQueryChanged: {
54 // Make sure we are at the search page if the search query changes behind our feet
56 headerContainer.showSearch = true;
60 function triggerSearch() {
61 if (searchEntryEnabled) {
62 headerContainer.showSearch = true;
63 searchTextField.forceActiveFocus();
67 function closePopup(keepFocus) {
68 if (headerContainer.popover != null) {
69 headerContainer.popover.unfocusOnDestruction = !keepFocus;
70 PopupUtils.close(headerContainer.popover);
71 } else if (!keepFocus) {
76 function resetSearch(keepFocus) {
78 searchHistory.addQuery(searchTextField.text);
80 searchTextField.text = "";
81 closePopup(keepFocus);
85 searchTextField.focus = false;
86 if (!searchTextField.text) {
87 headerContainer.showSearch = false;
91 function openSearchHistory() {
92 if (openSearchAnimation.running) {
93 openSearchAnimation.openSearchHistory = true;
94 } else if (root.searchHistory.count > 0 && headerContainer.popover == null) {
95 headerContainer.popover = PopupUtils.open(popoverComponent, searchTextField,
97 "contentWidth": searchTextField.width,
98 "edgeMargins": units.gu(1)
104 function refreshLogo() {
105 if (root.scopeStyle ? root.scopeStyle.headerLogo != "" : false) {
106 header.contents = imageComponent.createObject();
107 } else if (header.contents) {
108 header.contents.destroy();
109 header.contents = null;
114 target: root.scopeStyle
115 onHeaderLogoChanged: root.refreshLogo()
119 anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + bottomContainer.height }
120 visible: headerContainer.showSearch
122 closePopup(/* keepFocus */false);
123 if (!searchTextField.text) {
124 headerContainer.showSearch = false;
126 mouse.accepted = false;
132 objectName: "headerContainer"
133 clip: contentY < height
134 anchors { left: parent.left; top: parent.top; right: parent.right }
136 contentHeight: headersColumn.height
138 contentY: showSearch ? 0 : height
140 property bool showSearch: false
141 property var popover: null
144 objectName: "headerBackground"
145 style: scopeStyle.headerBackground
148 Behavior on contentY {
149 UbuntuNumberAnimation {
150 id: openSearchAnimation
151 property bool openSearchHistory: false
154 if (!running && openSearchAnimation.openSearchHistory) {
155 openSearchAnimation.openSearchHistory = false;
156 root.openSearchHistory();
164 anchors { left: parent.left; right: parent.right }
168 anchors { left: parent.left; right: parent.right }
169 height: headerContainer.height
170 contentHeight: height
171 opacity: headerContainer.clip || headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
172 __separator_visible: false
173 // Required to keep PageHeadStyle noise down as it expects the Page's properties around.
174 property var styledItem: searchHeader
175 property string title
176 property var config: PageHeadConfiguration {
177 foregroundColor: root.scopeStyle ? root.scopeStyle.headerForeground : Theme.palette.normal.baseText
182 headerContainer.showSearch = false;
186 property var contents: TextField {
188 objectName: "searchTextField"
189 inputMethodHints: Qt.ImhNoPredictiveText
190 hasClearButton: false
193 leftMargin: units.gu(1)
194 topMargin: units.gu(1)
195 bottomMargin: units.gu(1)
196 rightMargin: root.width > units.gu(60) ? root.width - units.gu(40) : units.gu(1)
199 secondaryItem: AbstractButton {
200 height: searchTextField.height
202 enabled: searchTextField.text.length > 0
205 objectName: "clearIcon"
207 anchors.margins: units.gu(.75)
208 source: "image://theme/clear"
209 opacity: searchTextField.text.length > 0
211 Behavior on opacity {
212 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
217 root.resetSearch(true);
218 root.openSearchHistory();
222 onActiveFocusChanged: {
224 root.openSearchHistory();
230 closePopup(/* keepFocus */true);
238 objectName: "innerPageHeader"
239 anchors { left: parent.left; right: parent.right }
240 height: headerContainer.height
241 contentHeight: height
242 opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
243 __separator_visible: false
244 property var styledItem: header
245 property string title: root.title
246 property var config: PageHeadConfiguration {
247 foregroundColor: root.scopeStyle ? root.scopeStyle.headerForeground : Theme.palette.normal.baseText
250 visible: root.showBackButton
251 onTriggered: root.backClicked()
257 text: i18n.tr("Search")
259 visible: root.searchEntryEnabled
261 headerContainer.showSearch = true;
262 searchTextField.forceActiveFocus();
266 objectName: "settings"
267 text: i18n.tr("Settings")
269 visible: root.settingsEnabled
270 onTriggered: root.settingsClicked()
273 objectName: "favorite"
274 text: root.favorite ? i18n.tr("Remove from Favorites") : i18n.tr("Add to Favorites")
275 iconName: root.favorite ? "starred" : "non-starred"
276 visible: root.favoriteEnabled
277 onTriggered: root.favoriteClicked()
282 property var contents: null
283 Component.onCompleted: root.refreshLogo()
289 anchors { fill: parent; topMargin: units.gu(1.5); bottomMargin: units.gu(1.5) }
292 objectName: "titleImage"
294 source: root.scopeStyle ? root.scopeStyle.headerLogo : ""
295 fillMode: Image.PreserveAspectFit
296 horizontalAlignment: Image.AlignLeft
297 sourceSize.height: height
311 property bool unfocusOnDestruction: false
313 Component.onDestruction: {
314 headerContainer.popover = null;
315 if (unfocusOnDestruction) {
329 objectName: "recentSearches"
333 showDivider: index < recentSearches.count - 1
336 searchHistory.addQuery(text);
337 searchTextField.text = text;
338 closePopup(/* keepFocus */false);
348 visible: showSignatureLine
350 top: headerContainer.bottom
353 bottom: bottomContainer.top
356 color: root.scopeStyle ? root.scopeStyle.headerDividerColor : "#e0e0e0"
365 color: Qt.darker(parent.color, 1.1)
370 visible: bottomBorder.visible
371 spacing: units.gu(.5)
373 objectName: "paginationRepeater"
374 model: root.paginationCount
376 objectName: "paginationDots_" + index
379 source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
383 top: headerContainer.bottom
384 horizontalCenter: headerContainer.horizontalCenter
385 topMargin: units.gu(.5)
389 // FIXME this doesn't work with solid scope backgrounds due to z-ordering
392 visible: bottomBorder.visible
394 top: bottomContainer.top
402 // FIXME this should be a shader when bottomItem exists
403 // to support image backgrounds
406 color: if (bottomItem && bottomItem.background) {
407 Qt.lighter(Qt.rgba(bottomItem.background.topColor.r,
408 bottomItem.background.topColor.g,
409 bottomItem.background.topColor.b, 1.0), 1.2);
410 } else if (!bottomItem && root.scopeStyle) {
411 Qt.lighter(Qt.rgba(root.scopeStyle.background.r,
412 root.scopeStyle.background.g,
413 root.scopeStyle.background.b, 1.0), 1.2);
424 bottom: parent.bottom
426 height: childrenRect.height