Unity 8
 All Classes Functions
Hud.qml
1 /*
2  * Copyright (C) 2012, 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 import HudClient 0.1
20 
21 import "../Components"
22 import "../Components/Flickables" as Flickables
23 
24 Item {
25  id: hud
26 
27  readonly property real elementsPadding: units.gu(1)
28  readonly property real elementsYSliding: units.gu(2)
29 
30  property alias revealerTarget: hudShowable
31  property alias showAnimation: hudShowable.showAnimation
32  property alias hideAnimation: hudShowable.hideAnimation
33  property alias available: hudShowable.available
34  property alias shown: hudShowable.shown
35  property alias handleHeight: handle.height
36 
37  readonly property variant outEasing: Easing.OutQuad
38  readonly property variant inEasing: Easing.InQuad
39  readonly property int animationDuration: 200
40  readonly property int showableAnimationDuration: 100
41  property bool showingOpenIndicator: false
42 
43  // FIXME At the moment since we have no appstack
44  // it's not possible to get results of the sidestage app
45  // design has to come up with a solution
46  function show() {
47  hudShowable.show()
48  }
49  function hide() {
50  hudShowable.hide()
51  }
52  function resetToInitialState() {
53  hud.state = "initial"
54  searchBar.unfocus()
55  searchBar.text = ""
56  parametrizedActionsPage.shown = false
57  }
58 
59  states: [
60  State {
61  name: "initial"
62  PropertyChanges { target: searchBar; placeholderText: i18n.tr("Type or say a command") }
63  PropertyChanges { target: searchBar; searchEnabled: true }
64  PropertyChanges { target: toolBarAnimator; visible: true}
65  AnchorChanges { target: searchBarAnimator; anchors.top: undefined; anchors.bottom: parent.bottom; }
66  AnchorChanges { target: resultsCardAnimator; anchors.top: undefined; anchors.bottom: toolBarAnimator.top; }
67  },
68  State {
69  name: "input" //only inherited by other states.
70  AnchorChanges { target: searchBarAnimator; anchors.top: parent.top; anchors.bottom: undefined; }
71  AnchorChanges { target: resultsCardAnimator; anchors.top: searchBarAnimator.bottom; anchors.bottom: undefined; }
72  PropertyChanges { target: toolBarAnimator; visible: false }
73  },
74  State {
75  name: "voice_input" //only inherited by other states.
76  extend: "input"
77  PropertyChanges { target: soundAmplitudeVisualAnimator; visible: true }
78  PropertyChanges { target: resultsCardAnimator; visible: false }
79  PropertyChanges { target: soundAmplitudeVisualAnimator; progress: 1 }
80  PropertyChanges { target: searchBar; searchEnabled: false }
81  },
82  State {
83  name: "voice_recognition_loading"
84  extend: "voice_input"
85  PropertyChanges { target: searchBar; placeholderText: i18n.tr("Loading. Please Wait...") }
86  },
87  State {
88  name: "voice_recognition_listening"
89  extend: "voice_input"
90  PropertyChanges { target: searchBar; placeholderText: i18n.tr("Speak Now...") }
91  },
92  State {
93  name: "voice_recognition_processing"
94  extend: "voice_input"
95  PropertyChanges { target: searchBar; placeholderText: i18n.tr("Speaking...") }
96  },
97  State {
98  name: "showing_results"
99  extend: "input"
100  PropertyChanges { target: searchBar; placeholderText: i18n.tr("Type or say a command") }
101  PropertyChanges { target: searchBar; searchEnabled: true }
102  }
103  ]
104 
105  transitions: [
106  Transition {
107  from: "initial"
108  to: "voice_recognition_loading"
109  SequentialAnimation {
110  NumberAnimation { // hide these components
111  targets: [toolBarAnimator, searchBarAnimator, resultsCardAnimator]
112  property: "progress"; duration: animationDuration; to: 0
113  }
114 
115  PropertyAction { targets: [toolBarAnimator, resultsCardAnimator]; property: "visible"; value: false}
116  PropertyAction { target: soundAmplitudeVisualAnimator; property: "visible"; value: true}
117 
118  AnchorAnimation { duration: 0 } // so anchor change happens at this point
119 
120  NumberAnimation { // show these components
121  targets: [searchBarAnimator, soundAmplitudeVisualAnimator]
122  property: "progress"; duration: animationDuration; from: 0; to: 1
123  }
124  }
125  },
126  Transition {
127  from: "showing_results"
128  to: "voice_recognition_loading"
129  SequentialAnimation {
130  PropertyAction { target: soundAmplitudeVisualAnimator; property: "progress"; value: 0}
131 
132  PropertyAction { // hide these components
133  target: resultsCardAnimator; property: "progress"; value: 0
134  }
135 
136  NumberAnimation { // show these components
137  target: soundAmplitudeVisualAnimator; property: "progress"; duration: animationDuration; from: 0; to: 1
138  }
139  }
140  },
141  Transition {
142  from: "voice_recognition_processing"
143  to: "showing_results"
144  SequentialAnimation {
145  NumberAnimation { // hide these components
146  target: soundAmplitudeVisualAnimator; property: "progress"; duration: animationDuration; to: 0
147  }
148  PropertyAction { target: resultsCardAnimator; property: "visible"; value: true}
149 
150  NumberAnimation { // show these components
151  target: resultsCardAnimator; property: "progress"; duration: animationDuration; from: 0; to: 1
152  }
153  }
154  },
155  Transition {
156  from: "initial"
157  to: "showing_results"
158  SequentialAnimation {
159  NumberAnimation {
160  targets: [toolBarAnimator, searchBarAnimator, resultsCardAnimator]
161  property: "progress"; duration: animationDuration; to: 0
162  }
163 
164  PropertyAction { target: toolBarAnimator; property: "visible"; value: false}
165 
166  AnchorAnimation { duration: 0 } // so anchor change happens at this point
167 
168  NumberAnimation {
169  targets: [searchBarAnimator, resultsCardAnimator]
170  property: "progress"; duration: animationDuration; from: 0; to: 1
171  }
172  }
173  }
174  ]
175 
176  state: "initial"
177 
178  HudClient {
179  id: hudClient
180 
181  onCommandExecuted: {
182  hudShowable.hide()
183  }
184 
185  onShowParametrizedAction: {
186  parametrizedActionsPage.header = action
187  parametrizedActionsPage.setItems(items)
188  parametrizedActionsPage.shown = true
189  }
190 
191  onVoiceQueryLoading: {
192  hud.state = "voice_recognition_loading"
193  searchBar.ignoreNextTextChange = true
194  searchBar.text = ""
195  searchBar.unfocus()
196  soundAmplitudeVisual.startIdle()
197  }
198  onVoiceQueryListening: {
199  if (hud.state == "voice_recognition_loading" || hud.state == "showing_results") {
200  searchBar.ignoreNextTextChange = true
201  searchBar.text = ""
202  searchBar.unfocus()
203  hud.state = "voice_recognition_listening"
204  }
205  }
206  onVoiceQueryHeardSomething: {
207  if (hud.state == "voice_recognition_listening") {
208  hud.state = "voice_recognition_processing"
209  soundAmplitudeVisual.setDetectorEnabled(true)
210  }
211  }
212  onVoiceQueryFinished: {
213  hud.state = "showing_results"
214  searchBar.text = query
215  soundAmplitudeVisual.setDetectorEnabled(false)
216  }
217  onVoiceQueryFailed: {
218  hud.state = "showing_results"
219  searchBar.text = ""
220  soundAmplitudeVisual.setDetectorEnabled(false)
221  }
222  }
223 
224  Showable {
225  id: hudShowable
226  objectName: "hudShowable"
227  height: parent.height
228  width: parent.width
229 
230  onYChanged: {
231  if (greeter.shown) {
232  showAnimation.duration = 0
233  hideAnimation.duration = 0
234  } else if (!showAnimation.running && !hideAnimation.running) {
235  if (parent.height > 0) {
236  showAnimation.duration = Math.min(showableAnimationDuration * (1 - (parent.height - y) / parent.height), showableAnimationDuration)
237  hideAnimation.duration = showableAnimationDuration - showAnimation.duration
238  }
239  }
240  }
241 
242  MouseArea {
243  // Eat everything that doesn't go to other places
244  anchors.fill: parent
245  }
246 
247  Image {
248  anchors.fill: parent
249  fillMode: Image.Tile
250  source: "graphics/hud_bg.png"
251  }
252 
253  Connections {
254  target: hideAnimation
255  onRunningChanged: {
256  if (!hideAnimation.running) {
257  showAnimation.duration = showableAnimationDuration
258  hud.resetToInitialState()
259  }
260  }
261  }
262 
263  Connections {
264  target: showAnimation
265  onRunningChanged: {
266  if (!showAnimation.running) {
267  hideAnimation.duration = showableAnimationDuration
268  }
269  }
270  }
271  }
272 
273  Item {
274  id: handle
275  objectName: "handle"
276 
277  y: hudShowable.y
278  anchors {
279  left: parent.left
280  right: parent.right
281  }
282  height: handleImage.height
283 
284  Image {
285  id: handleImage
286  anchors {
287  left: parent.left
288  right: parent.right
289  }
290  source: "graphics/hud_handlebar.png"
291  }
292 
293  Image {
294  id: handleArrow
295  y: units.gu(1)
296  anchors.horizontalCenter: parent.horizontalCenter
297  source: "graphics/hud_handlearrow.png"
298  cache: false
299  }
300  }
301 
302  Item {
303  id: hudContentClipper
304 
305  anchors {
306  left: parent.left
307  right: parent.right
308  bottom: parent.bottom
309  top: handle.bottom
310  }
311  clip: visible && hudContent.height !== height
312  visible: hudContent.height >= 0
313 
314  Item {
315  id: hudContent
316  anchors {
317  left: parent.left
318  right: parent.right
319  bottom: parent.bottom
320  }
321  height: hud.height
322 
323  Item {
324  id: mainPage
325  x: parametrizedActionsPage.x - width
326  height: hud.height
327  width: hud.width
328 
329  ShowingAnimation {
330  id: toolBarAnimator
331  objectName: "toolBarAnimator"
332  anchors {
333  left: parent.left
334  right: parent.right
335  bottom: searchBarAnimator.top
336  margins: 2*elementsPadding //ensures positioning correct
337  }
338  progress: MathUtils.clamp((y - hudShowable.y - anchors.margins)/elementsYSliding, 0, 1)
339 
340  ToolBar {
341  id: toolBar
342  objectName: "toolBar"
343  model: hudClient.toolBarModel
344  anchors.horizontalCenter: parent.horizontalCenter
345  onActionTriggered: {
346  hudClient.executeToolBarAction(action)
347  }
348  }
349  }
350 
351  ShowingAnimation {
352  id: searchBarAnimator
353  objectName: "searchBarAnimator"
354  anchors {
355  left: parent.left
356  right: parent.right
357  bottom: parent.bottom
358  margins: elementsPadding
359  topMargin: handle.height + units.dp(1) + elementsPadding
360  }
361  progress: MathUtils.clamp((y - hudShowable.y - anchors.margins)/elementsYSliding, 0, 1)
362 
363  SearchBar {
364  id: searchBar
365  objectName: "searchBar"
366 
367  property bool ignoreNextTextChange: false
368 
369  anchors.left: parent.left
370  anchors.right: parent.right
371  height: units.gu(5)
372 
373  placeholderText: i18n.tr("Type or say a command")
374  activityIndicatorVisible: hud.state == "voice_recognition_processing"
375 
376  onMicrophoneClicked: hudClient.startVoiceQuery()
377 
378  onTextChanged: {
379  if (ignoreNextTextChange) {
380  ignoreNextTextChange = false
381  } else {
382  hudClient.setQuery(searchBar.text)
383  }
384  }
385 
386  onTextFocused: {
387  hud.state = "showing_results"
388  }
389  }
390  }
391 
392  ShowingAnimation {
393  id: resultsCardAnimator
394  objectName: "resultsCardAnimator"
395 
396  anchors {
397  left: parent.left
398  right: parent.right
399  top: searchBarAnimator.bottom
400  margins: elementsPadding
401  }
402  progress: MathUtils.clamp((y - hudShowable.y + height - units.gu(8))/elementsYSliding, 0, 1)
403 
404  Flickables.Flickable {
405  anchors.left: parent.left
406  anchors.right: parent.right
407  contentHeight: resultList.height
408  contentWidth: width
409  clip: height < contentHeight
410  interactive: height < contentHeight
411 
412  height: {
413  if (hud.state == "showing_results") {
414  return shell.applicationManager.keyboardVisible ? Math.min(hud.height - searchBarAnimator.y - searchBarAnimator.height - units.gu(2) - shell.applicationManager.keyboardHeight, contentHeight) : contentHeight
415  } else {
416  return contentHeight
417  }
418  }
419 
420 
421  ResultList {
422  id: resultList
423 
424  anchors.left: parent.left
425  anchors.right: parent.right
426  height: childrenRect.height
427 
428  onActivated: {
429  hudClient.executeCommand(index)
430  }
431 
432  model: hudClient.results
433  }
434  }
435  }
436 
437  ShowingAnimation {
438  id: soundAmplitudeVisualAnimator
439 
440  anchors {
441  horizontalCenter: parent.horizontalCenter
442  verticalCenter: parent.verticalCenter
443  verticalCenterOffset: (searchBar.height + 2*elementsPadding)/2
444  }
445  visible: false
446  width: units.gu(33)
447  progress: MathUtils.clamp((y - hudShowable.y - anchors.verticalCenterOffset)/elementsYSliding, 0, 1)
448 
449  SoundAmplitudeVisual {
450  id: soundAmplitudeVisual
451  width: units.gu(33)
452  height: width
453  }
454  }
455  }
456 
457  HudParametrizedActionsPage {
458  id: parametrizedActionsPage
459  objectName: "parametrizedActionsPage"
460  property bool shown: false
461 
462  anchors.bottom: parent.bottom
463  height: hud.height - handle.height - units.dp(1)
464  width: parent.width
465  x: shown ? 0 : width
466  onConfirmPressed: {
467  hudClient.executeParametrizedAction(values())
468  }
469  onValuesUpdated: {
470  hudClient.updateParametrizedAction(values())
471  }
472  onBackPressed: {
473  shown = false
474  }
475  onShownChanged: {
476  if (!shown) {
477  hudClient.cancelParametrizedAction()
478  }
479  }
480  Behavior on x {
481  NumberAnimation {
482  easing.type: outEasing
483  duration: animationDuration
484  }
485  }
486  }
487  }
488  }
489 
490  Image {
491  anchors.left: hudContentClipper.right
492  anchors.top: hudContentClipper.top
493  anchors.bottom: hudContentClipper.bottom
494  fillMode: Image.Tile
495  source: "../graphics/dropshadow_right.png"
496  }
497 }