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"
24 import "../Components/Flickables" as Flickables
28 objectName: "pageHeader"
29 implicitHeight: headerContainer.height + bottomContainer.height + (showSignatureLine ? units.gu(2) : 0)
31 property bool showBackButton: false
34 property bool searchEntryEnabled: false
35 property bool settingsEnabled: false
36 property bool favoriteEnabled: false
37 property bool favorite: false
38 property ListModel searchHistory: SearchHistoryModel
39 property alias searchQuery: searchTextField.text
40 property alias searchHint: searchTextField.placeholderText
41 property bool showSignatureLine: true
43 property alias bottomItem: bottomContainer.children
44 property int paginationCount: 0
45 property int paginationIndex: -1
47 property var scopeStyle: null
50 signal settingsClicked()
51 signal favoriteClicked()
53 onScopeStyleChanged: refreshLogo()
54 onSearchQueryChanged: {
55 // Make sure we are at the search page if the search query changes behind our feet
57 headerContainer.showSearch = true;
61 function triggerSearch() {
62 if (searchEntryEnabled) {
63 headerContainer.showSearch = true;
64 searchTextField.forceActiveFocus();
68 function closePopup(keepFocus) {
69 if (headerContainer.popover != null) {
70 headerContainer.popover.unfocusOnDestruction = !keepFocus;
71 PopupUtils.close(headerContainer.popover);
72 } else if (!keepFocus) {
77 function resetSearch(keepFocus) {
79 searchHistory.addQuery(searchTextField.text);
81 searchTextField.text = "";
82 closePopup(keepFocus);
86 searchTextField.focus = false;
87 if (!searchTextField.text) {
88 headerContainer.showSearch = false;
92 function openSearchHistory() {
93 if (openSearchAnimation.running) {
94 openSearchAnimation.openSearchHistory = true;
95 } else if (root.searchHistory.count > 0 && headerContainer.popover == null) {
96 headerContainer.popover = PopupUtils.open(popoverComponent, searchTextField,
98 "contentWidth": searchTextField.width,
99 "edgeMargins": units.gu(1)
105 function refreshLogo() {
106 if (root.scopeStyle ? root.scopeStyle.headerLogo != "" : false) {
107 header.contents = imageComponent.createObject();
108 } else if (header.contents) {
109 header.contents.destroy();
110 header.contents = null;
115 target: root.scopeStyle
116 onHeaderLogoChanged: root.refreshLogo()
120 anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + bottomContainer.height }
121 visible: headerContainer.showSearch
123 closePopup(/* keepFocus */false);
124 if (!searchTextField.text) {
125 headerContainer.showSearch = false;
127 mouse.accepted = false;
131 Flickables.Flickable {
133 objectName: "headerContainer"
134 clip: contentY < height
135 anchors { left: parent.left; top: parent.top; right: parent.right }
137 contentHeight: headersColumn.height
139 contentY: showSearch ? 0 : height
141 property bool showSearch: false
142 property var popover: null
145 objectName: "headerBackground"
146 style: scopeStyle.headerBackground
149 Behavior on contentY {
150 UbuntuNumberAnimation {
151 id: openSearchAnimation
152 property bool openSearchHistory: false
155 if (!running && openSearchAnimation.openSearchHistory) {
156 openSearchAnimation.openSearchHistory = false;
157 root.openSearchHistory();
165 anchors { left: parent.left; right: parent.right }
169 anchors { left: parent.left; right: parent.right }
170 height: headerContainer.height
171 contentHeight: height
172 opacity: headerContainer.clip || headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
174 // Required to keep PageHeadStyle noise down as it expects the Page's properties around.
175 property var styledItem: searchHeader
176 property string title
177 property var config: PageHeadConfiguration {
178 foregroundColor: root.scopeStyle ? root.scopeStyle.headerForeground : Theme.palette.normal.baseText
183 headerContainer.showSearch = false;
187 property var contents: TextField {
189 objectName: "searchTextField"
190 inputMethodHints: Qt.ImhNoPredictiveText
191 hasClearButton: false
194 leftMargin: units.gu(1)
195 topMargin: units.gu(1)
196 bottomMargin: units.gu(1)
197 rightMargin: root.width > units.gu(60) ? root.width - units.gu(40) : units.gu(1)
200 secondaryItem: AbstractButton {
201 height: searchTextField.height
203 enabled: searchTextField.text.length > 0
206 objectName: "clearIcon"
208 anchors.margins: units.gu(.75)
209 source: "image://theme/clear"
210 opacity: searchTextField.text.length > 0
212 Behavior on opacity {
213 UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
218 root.resetSearch(true);
219 root.openSearchHistory();
223 onActiveFocusChanged: {
225 root.openSearchHistory();
231 closePopup(/* keepFocus */true);
239 objectName: "innerPageHeader"
240 anchors { left: parent.left; right: parent.right }
241 height: headerContainer.height
242 contentHeight: height
243 opacity: headerContainer.clip || !headerContainer.showSearch ? 1 : 0 // setting visible false cause column to relayout
245 separatorBottomSource: ""
246 property var styledItem: header
247 property string title: root.title
248 property var config: PageHeadConfiguration {
249 foregroundColor: root.scopeStyle ? root.scopeStyle.headerForeground : Theme.palette.normal.baseText
252 visible: root.showBackButton
253 onTriggered: root.backClicked()
259 text: i18n.tr("Search")
261 visible: root.searchEntryEnabled
263 headerContainer.showSearch = true;
264 searchTextField.forceActiveFocus();
268 objectName: "settings"
269 text: i18n.tr("Settings")
271 visible: root.settingsEnabled
272 onTriggered: root.settingsClicked()
275 objectName: "favorite"
276 text: root.favorite ? i18n.tr("Remove from Favorites") : i18n.tr("Add to Favorites")
277 iconName: root.favorite ? "starred" : "non-starred"
278 visible: root.favoriteEnabled
279 onTriggered: root.favoriteClicked()
284 property var contents: null
285 Component.onCompleted: root.refreshLogo()
291 anchors { fill: parent; topMargin: units.gu(1.5); bottomMargin: units.gu(1.5) }
294 objectName: "titleImage"
296 source: root.scopeStyle ? root.scopeStyle.headerLogo : ""
297 fillMode: Image.PreserveAspectFit
298 horizontalAlignment: Image.AlignLeft
299 sourceSize.height: height
313 property bool unfocusOnDestruction: false
315 Component.onDestruction: {
316 headerContainer.popover = null;
317 if (unfocusOnDestruction) {
331 objectName: "recentSearches"
335 showDivider: index < recentSearches.count - 1
338 searchHistory.addQuery(text);
339 searchTextField.text = text;
340 closePopup(/* keepFocus */false);
350 visible: showSignatureLine
352 top: headerContainer.bottom
355 bottom: bottomContainer.top
358 color: root.scopeStyle ? root.scopeStyle.headerDividerColor : "#e0e0e0"
367 color: Qt.darker(parent.color, 1.1)
372 visible: bottomBorder.visible
373 spacing: units.gu(.5)
375 objectName: "paginationRepeater"
376 model: root.paginationCount
378 objectName: "paginationDots_" + index
381 source: (index == root.paginationIndex) ? "graphics/pagination_dot_on.png" : "graphics/pagination_dot_off.png"
385 top: headerContainer.bottom
386 horizontalCenter: headerContainer.horizontalCenter
387 topMargin: units.gu(.5)
391 // FIXME this doesn't work with solid scope backgrounds due to z-ordering
394 visible: bottomBorder.visible
396 top: bottomContainer.top
404 // FIXME this should be a shader when bottomItem exists
405 // to support image backgrounds
408 color: if (bottomItem && bottomItem.background) {
409 Qt.lighter(Qt.rgba(bottomItem.background.topColor.r,
410 bottomItem.background.topColor.g,
411 bottomItem.background.topColor.b, 1.0), 1.2);
412 } else if (!bottomItem && root.scopeStyle) {
413 Qt.lighter(Qt.rgba(root.scopeStyle.background.r,
414 root.scopeStyle.background.g,
415 root.scopeStyle.background.b, 1.0), 1.2);
426 bottom: parent.bottom
428 height: childrenRect.height