Unity 8
 All Classes Functions Properties
PageHeader.qml
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.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"
23 
24 Item {
25  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 
32  property bool searchEntryEnabled: false
33  property ListModel searchHistory: SearchHistoryModel
34  property alias searchQuery: searchTextField.text
35  property bool searchInProgress: false
36 
37  property alias bottomItem: bottomContainer.children
38 
39  // TODO We should use foreground for the icons
40  // of the toolbar but unfortunately Action does not have
41  // the keyColor property as Icon does :-/
42  property var scopeStyle: null
43 
44  signal backClicked()
45 
46  onScopeStyleChanged: refreshLogo()
47 
48  function triggerSearch() {
49  if (searchEntryEnabled) {
50  headerContainer.showSearch = true;
51  searchTextField.forceActiveFocus();
52  }
53  }
54 
55  function resetSearch(keepFocus) {
56  if (searchHistory) {
57  searchHistory.addQuery(searchTextField.text);
58  }
59  if (!keepFocus) {
60  unfocus();
61  }
62  searchTextField.text = "";
63  if (headerContainer.popover != null) {
64  PopupUtils.close(headerContainer.popover);
65  }
66  }
67 
68  function unfocus() {
69  searchTextField.focus = false;
70  if (!searchTextField.text) {
71  headerContainer.showSearch = false;
72  }
73  }
74 
75  function openSearchHistory() {
76  if (openSearchAnimation.running) {
77  openSearchAnimation.openSearchHistory = true;
78  } else if (root.searchHistory.count > 0 && headerContainer.popover == null) {
79  headerContainer.popover = PopupUtils.open(popoverComponent, searchTextField,
80  {
81  "contentWidth": searchTextField.width,
82  "edgeMargins": units.gu(1)
83  }
84  );
85  }
86  }
87 
88  function refreshLogo() {
89  if (scopeStyle ? scopeStyle.headerLogo != "" : false) {
90  header.contents = imageComponent.createObject();
91  } else if (header.contents) {
92  header.contents.destroy();
93  header.contents = null;
94  }
95  }
96 
97  Connections {
98  target: root.scopeStyle
99  onHeaderLogoChanged: root.refreshLogo()
100  }
101 
102  InverseMouseArea {
103  anchors { fill: parent; margins: units.gu(1); bottomMargin: units.gu(3) + bottomContainer.height }
104  visible: headerContainer.showSearch
105  onPressed: {
106  if (headerContainer.popover) {
107  PopupUtils.close(headerContainer.popover);
108  }
109  if (!searchTextField.text) {
110  headerContainer.showSearch = false;
111  }
112  searchTextField.focus = false;
113  mouse.accepted = false;
114  }
115  }
116 
117  Flickable {
118  id: headerContainer
119  objectName: "headerContainer"
120  clip: true
121  anchors { left: parent.left; top: parent.top; right: parent.right }
122  height: units.gu(6.5)
123  contentHeight: headersColumn.height
124  interactive: false
125  contentY: showSearch ? 0 : height
126 
127  property bool showSearch: false
128  property var popover: null
129 
130  Behavior on contentY {
131  UbuntuNumberAnimation {
132  id: openSearchAnimation
133  property bool openSearchHistory: false
134 
135  onRunningChanged: {
136  if (!running && openSearchAnimation.openSearchHistory) {
137  openSearchAnimation.openSearchHistory = false;
138  root.openSearchHistory();
139  }
140  }
141  }
142  }
143 
144  Column {
145  id: headersColumn
146  anchors { left: parent.left; right: parent.right }
147 
148  PageHeadStyle {
149  id: searchHeader
150  anchors { left: parent.left; right: parent.right }
151  height: headerContainer.height
152  contentHeight: height
153  separatorSource: ""
154  // Required to keep PageHeadStyle noise down as it expects the Page's properties around.
155  property var styledItem: searchHeader
156  property string title
157  property var config: PageHeadConfiguration {
158  backAction: Action {
159  iconName: "back"
160  onTriggered: {
161  root.resetSearch();
162  headerContainer.showSearch = false;
163  }
164  }
165  }
166  property var contents: TextField {
167  id: searchTextField
168  objectName: "searchTextField"
169  hasClearButton: false
170  anchors {
171  fill: parent
172  leftMargin: units.gu(1)
173  topMargin: units.gu(1)
174  bottomMargin: units.gu(1)
175  rightMargin: root.width > units.gu(60) ? root.width - units.gu(40) : units.gu(1)
176  }
177 
178  secondaryItem: AbstractButton {
179  height: searchTextField.height
180  width: height
181  enabled: searchTextField.text.length > 0
182 
183  Image {
184  objectName: "clearIcon"
185  anchors.fill: parent
186  anchors.margins: units.gu(.75)
187  source: "image://theme/clear"
188  opacity: searchTextField.text.length > 0 && !searchActivityIndicator.running
189  visible: opacity > 0
190  Behavior on opacity {
191  UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
192  }
193  }
194 
195  ActivityIndicator {
196  id: searchActivityIndicator
197  objectName: "searchIndicator"
198  anchors.fill: parent
199  anchors.margins: units.gu(.75)
200  running: root.searchInProgress
201  opacity: running ? 1 : 0
202  Behavior on opacity {
203  UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
204  }
205  }
206 
207  onClicked: {
208  root.resetSearch(true);
209  root.openSearchHistory();
210  }
211  }
212 
213  onActiveFocusChanged: {
214  if (activeFocus) {
215  root.openSearchHistory();
216  }
217  }
218  }
219  }
220 
221  PageHeadStyle {
222  id: header
223  objectName: "innerPageHeader"
224  anchors { left: parent.left; right: parent.right }
225  height: headerContainer.height
226  contentHeight: height
227  separatorSource: ""
228  textColor: root.scopeStyle ? root.scopeStyle.foreground : "grey"
229  property var styledItem: header
230  property string title: root.title
231  property var config: PageHeadConfiguration {
232  backAction: Action {
233  iconName: "back"
234  visible: root.showBackButton
235  onTriggered: {
236  root.backClicked();
237  }
238  }
239 
240  actions: [
241  Action {
242  iconName: "search"
243  visible: root.searchEntryEnabled
244  onTriggered: {
245  headerContainer.showSearch = true;
246  searchTextField.forceActiveFocus();
247  }
248  }
249  ]
250  }
251 
252  property var contents: null
253  Component.onCompleted: root.refreshLogo()
254 
255  Component {
256  id: imageComponent
257 
258  Item {
259  anchors { fill: parent; topMargin: units.gu(1); bottomMargin: units.gu(1) }
260  clip: true
261  Image {
262  objectName: "titleImage"
263  anchors.fill: parent
264  source: root.scopeStyle ? root.scopeStyle.headerLogo : ""
265  fillMode: Image.PreserveAspectFit
266  horizontalAlignment: Image.AlignLeft
267  sourceSize.height: height
268  }
269  }
270  }
271  }
272  }
273  }
274 
275  Component {
276  id: popoverComponent
277  Popover {
278  id: popover
279  autoClose: false
280 
281  Component.onDestruction: {
282  headerContainer.popover = null;
283  }
284 
285  Column {
286  anchors {
287  top: parent.top
288  left: parent.left
289  right: parent.right
290  }
291 
292  Repeater {
293  id: recentSearches
294  model: searchHistory
295 
296  delegate: Standard {
297  showDivider: index < recentSearches.count - 1
298  text: query
299  onClicked: {
300  searchHistory.addQuery(text);
301  searchTextField.text = text;
302  PopupUtils.close(popover);
303  }
304  }
305  }
306  }
307  }
308  }
309 
310  BorderImage {
311  id: bottomBorder
312  anchors {
313  top: headerContainer.bottom
314  left: parent.left
315  right: parent.right
316  bottom: bottomContainer.top
317  }
318 
319  source: "graphics/PageHeaderBaseDivider.sci"
320  }
321 
322  Item {
323  id: bottomContainer
324 
325  anchors {
326  left: parent.left
327  right: parent.right
328  bottom: parent.bottom
329  }
330  height: childrenRect.height
331  }
332 }