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