Unity 8
Panel.qml
1 /*
2  * Copyright (C) 2013-2017 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.4
18 import Ubuntu.Components 1.3
19 import Ubuntu.Layouts 1.0
20 import Unity.Application 0.1
21 import Unity.Indicators 0.1
22 import Utils 0.1
23 import Unity.ApplicationMenu 0.1
24 
25 import QtQuick.Window 2.2
26 
27 import "../ApplicationMenus"
28 import "../Components"
29 import "../Components/PanelState"
30 import ".."
31 import "Indicators"
32 
33 Item {
34  id: root
35  readonly property real panelHeight: panelArea.y + minimizedPanelHeight
36 
37  property real minimizedPanelHeight: units.gu(3)
38  property real expandedPanelHeight: units.gu(7)
39  property real indicatorMenuWidth: width
40  property real applicationMenuWidth: width
41  property alias applicationMenuContentX: __applicationMenus.menuContentX
42 
43  property alias applicationMenus: __applicationMenus
44  property alias indicators: __indicators
45  property bool fullscreenMode: false
46  property real panelAreaShowProgress: 1.0
47  property bool greeterShown: false
48  property bool hasKeyboard: false
49 
50  property string mode: "staged"
51 
52  MouseArea {
53  id: backMouseEater
54  anchors.fill: parent
55  anchors.topMargin: panelHeight
56  visible: !indicators.fullyClosed || !applicationMenus.fullyClosed
57  enabled: visible
58  hoverEnabled: true // should also eat hover events, otherwise they will pass through
59 
60  onClicked: {
61  __applicationMenus.hide();
62  __indicators.hide();
63  }
64  }
65 
66  Binding {
67  target: PanelState
68  property: "panelHeight"
69  value: minimizedPanelHeight
70  }
71 
72  RegisteredApplicationMenuModel {
73  id: registeredMenuModel
74  persistentSurfaceId: PanelState.focusedPersistentSurfaceId
75  }
76 
77  QtObject {
78  id: d
79 
80  property bool revealControls: !greeterShown &&
81  !applicationMenus.shown &&
82  !indicators.shown &&
83  (decorationMouseArea.containsMouse || menuBarLoader.menusRequested)
84 
85  property bool showWindowDecorationControls: (revealControls && PanelState.decorationsVisible) ||
86  PanelState.decorationsAlwaysVisible
87 
88  property bool showPointerMenu: revealControls && enablePointerMenu &&
89  (PanelState.decorationsVisible || mode == "staged")
90 
91  property bool enablePointerMenu: applicationMenus.available &&
92  applicationMenus.model
93 
94  property bool showTouchMenu: !greeterShown &&
95  !showPointerMenu &&
96  !showWindowDecorationControls
97 
98  property bool enableTouchMenus: showTouchMenu &&
99  applicationMenus.available &&
100  applicationMenus.model
101  }
102 
103  Item {
104  id: panelArea
105  objectName: "panelArea"
106 
107  anchors.fill: parent
108 
109  transform: Translate {
110  y: indicators.state === "initial"
111  ? (1.0 - panelAreaShowProgress) * - minimizedPanelHeight
112  : 0
113  }
114 
115  BorderImage {
116  id: indicatorsDropShadow
117  anchors {
118  fill: __indicators
119  margins: -units.gu(1)
120  }
121  visible: !__indicators.fullyClosed
122  source: "graphics/rectangular_dropshadow.sci"
123  }
124 
125  BorderImage {
126  id: appmenuDropShadow
127  anchors {
128  fill: __applicationMenus
129  margins: -units.gu(1)
130  }
131  visible: !__applicationMenus.fullyClosed
132  source: "graphics/rectangular_dropshadow.sci"
133  }
134 
135  BorderImage {
136  id: panelDropShadow
137  anchors {
138  fill: panelAreaBackground
139  bottomMargin: -units.gu(1)
140  }
141  visible: PanelState.dropShadow
142  source: "graphics/rectangular_dropshadow.sci"
143  }
144 
145  Rectangle {
146  id: panelAreaBackground
147  color: callHint.visible ? theme.palette.normal.positive : theme.palette.normal.background
148  anchors {
149  top: parent.top
150  left: parent.left
151  right: parent.right
152  }
153  height: minimizedPanelHeight
154 
155  Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
156  }
157 
158  MouseArea {
159  id: decorationMouseArea
160  objectName: "windowControlArea"
161  anchors {
162  left: parent.left
163  right: parent.right
164  }
165  height: minimizedPanelHeight
166  hoverEnabled: !__indicators.shown
167  onClicked: {
168  if (callHint.visible) {
169  callHint.showLiveCall();
170  }
171  }
172 
173  onPressed: {
174  if (!callHint.visible) {
175  // let it fall through to the window decoration of the maximized window behind, if any
176  mouse.accepted = false;
177  }
178  var menubar = menuBarLoader.item;
179  if (menubar) {
180  menubar.invokeMenu(mouse);
181  }
182  }
183 
184  Row {
185  anchors.fill: parent
186  spacing: units.gu(2)
187 
188  // WindowControlButtons inside the mouse area, otherwise QML doesn't grok nested hover events :/
189  // cf. https://bugreports.qt.io/browse/QTBUG-32909
190  WindowControlButtons {
191  id: windowControlButtons
192  objectName: "panelWindowControlButtons"
193  height: indicators.minimizedPanelHeight
194  opacity: d.showWindowDecorationControls ? 1 : 0
195  visible: opacity != 0
196  Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
197 
198  active: PanelState.decorationsVisible || PanelState.decorationsAlwaysVisible
199  windowIsMaximized: true
200  onCloseClicked: PanelState.closeClicked()
201  onMinimizeClicked: PanelState.minimizeClicked()
202  onMaximizeClicked: PanelState.restoreClicked()
203  closeButtonShown: PanelState.closeButtonShown
204  }
205 
206  Loader {
207  id: menuBarLoader
208  objectName: "menuBarLoader"
209  height: parent.height
210  enabled: d.enablePointerMenu
211  opacity: d.showPointerMenu ? 1 : 0
212  visible: opacity != 0
213  Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
214  active: d.showPointerMenu && !callHint.visible
215 
216  width: parent.width - windowControlButtons.width - units.gu(2) - __indicators.barWidth
217 
218  readonly property bool menusRequested: menuBarLoader.item ? menuBarLoader.item.showRequested : false
219 
220  sourceComponent: MenuBar {
221  id: bar
222  objectName: "menuBar"
223  anchors.left: parent ? parent.left : undefined
224  anchors.margins: units.gu(1)
225  height: menuBarLoader.height
226  enableKeyFilter: valid && PanelState.decorationsVisible
227  unityMenuModel: __applicationMenus.model
228 
229  Connections {
230  target: __applicationMenus
231  onShownChanged: bar.dismiss();
232  }
233 
234  Connections {
235  target: __indicators
236  onShownChanged: bar.dismiss();
237  }
238 
239  onDoubleClicked: PanelState.restoreClicked()
240  onPressed: mouse.accepted = false // let the parent mouse area handle this, so it can both unsnap window and show menu
241  }
242  }
243  }
244 
245  ActiveCallHint {
246  id: callHint
247  objectName: "callHint"
248 
249  anchors.centerIn: parent
250  height: minimizedPanelHeight
251 
252  visible: active && indicators.state == "initial" && __applicationMenus.state == "initial"
253  greeterShown: root.greeterShown
254  }
255  }
256 
257  PanelMenu {
258  id: __applicationMenus
259 
260  x: menuContentX
261  model: registeredMenuModel.model
262  width: root.applicationMenuWidth
263  minimizedPanelHeight: root.minimizedPanelHeight
264  expandedPanelHeight: root.expandedPanelHeight
265  openedHeight: root.height
266  alignment: Qt.AlignLeft
267  enableHint: !callHint.active && !fullscreenMode
268  showOnClick: false
269  adjustDragHandleSizeToContents: false
270  panelColor: panelAreaBackground.color
271 
272  onShowTapped: {
273  if (callHint.active) {
274  callHint.showLiveCall();
275  }
276  }
277 
278  hideRow: !expanded
279  rowItemDelegate: ActionItem {
280  id: actionItem
281  property int ownIndex: index
282  objectName: "appMenuItem"+index
283  enabled: model.sensitive
284 
285  width: _title.width + units.gu(2)
286  height: parent.height
287 
288  action: Action {
289  text: model.label.replace("_", "&")
290  }
291 
292  Label {
293  id: _title
294  anchors.centerIn: parent
295  text: actionItem.text
296  horizontalAlignment: Text.AlignLeft
297  color: enabled ? theme.palette.normal.backgroundText : theme.palette.disabled.backgroundText
298  }
299  }
300 
301  pageDelegate: PanelMenuPage {
302  readonly property bool isCurrent: modelIndex == __applicationMenus.currentMenuIndex
303  onIsCurrentChanged: {
304  if (isCurrent && menuModel) {
305  menuModel.aboutToShow(modelIndex);
306  }
307  }
308 
309  menuModel: __applicationMenus.model
310  submenuIndex: modelIndex
311 
312  factory: ApplicationMenuItemFactory {
313  rootModel: __applicationMenus.model
314  }
315  }
316 
317  enabled: d.enableTouchMenus
318  opacity: d.showTouchMenu ? 1 : 0
319  visible: opacity != 0
320  Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
321 
322  onEnabledChanged: {
323  if (!enabled) hide();
324  }
325  }
326 
327  Label {
328  id: rowLabel
329  objectName: "panelTitle"
330  anchors {
331  left: parent.left
332  leftMargin: units.gu(1)
333  right: __indicators.left
334  rightMargin: units.gu(1)
335  }
336  height: root.minimizedPanelHeight
337  verticalAlignment: Text.AlignVCenter
338  elide: Text.ElideRight
339  maximumLineCount: 1
340  fontSize: "medium"
341  font.weight: Font.Medium
342  color: theme.palette.selected.backgroundText
343  opacity: __applicationMenus.visible && !__applicationMenus.expanded ? 1 : 0
344  visible: opacity != 0
345  Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
346  text: PanelState.title
347  }
348 
349  PanelMenu {
350  id: __indicators
351  objectName: "indicators"
352 
353  anchors {
354  top: parent.top
355  right: parent.right
356  }
357  width: root.indicatorMenuWidth
358  minimizedPanelHeight: root.minimizedPanelHeight
359  expandedPanelHeight: root.expandedPanelHeight
360  openedHeight: root.height
361 
362  overFlowWidth: root.width
363  enableHint: !callHint.active && !fullscreenMode
364  showOnClick: !callHint.visible
365  panelColor: panelAreaBackground.color
366 
367  onShowTapped: {
368  if (callHint.active) {
369  callHint.showLiveCall();
370  }
371  }
372 
373  rowItemDelegate: IndicatorItem {
374  id: indicatorItem
375  objectName: identifier+"-panelItem"
376 
377  property int ownIndex: index
378  readonly property bool overflow: parent.width - x > __indicators.overFlowWidth
379  readonly property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator || hideKeyboardIndicator)
380  // HACK for indicator-session
381  readonly property bool hideSessionIndicator: identifier == "indicator-session" && Math.min(Screen.width, Screen.height) <= units.gu(60)
382  // HACK for indicator-keyboard
383  readonly property bool hideKeyboardIndicator: identifier == "indicator-keyboard" && !hasKeyboard
384 
385  height: parent.height
386  expanded: indicators.expanded
387  selected: ListView.isCurrentItem
388 
389  identifier: model.identifier
390  busName: indicatorProperties.busName
391  actionsObjectPath: indicatorProperties.actionsObjectPath
392  menuObjectPath: indicatorProperties.menuObjectPath
393 
394  opacity: hidden ? 0.0 : 1.0
395  Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
396 
397  width: ((expanded || indicatorVisible) && !hideSessionIndicator && !hideKeyboardIndicator) ? implicitWidth : 0
398 
399  Behavior on width { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
400  }
401 
402  pageDelegate: PanelMenuPage {
403  objectName: modelData.identifier + "-page"
404  submenuIndex: 0
405 
406  menuModel: delegate.menuModel
407 
408  factory: IndicatorMenuItemFactory {
409  indicator: {
410  var context = modelData.identifier;
411  if (context && context.indexOf("fake-") === 0) {
412  context = context.substring("fake-".length)
413  }
414  return context;
415  }
416  rootModel: delegate.menuModel
417  }
418 
419  IndicatorDelegate {
420  id: delegate
421  busName: modelData.indicatorProperties.busName
422  actionsObjectPath: modelData.indicatorProperties.actionsObjectPath
423  menuObjectPath: modelData.indicatorProperties.menuObjectPath
424  }
425  }
426 
427  enabled: !applicationMenus.expanded
428  opacity: !callHint.visible && !applicationMenus.expanded ? 1 : 0
429  Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
430 
431  onEnabledChanged: {
432  if (!enabled) hide();
433  }
434  }
435  }
436 
437  IndicatorsLight {
438  id: indicatorLights
439  }
440 
441  states: [
442  State {
443  name: "onscreen" //fully opaque and visible at top edge of screen
444  when: !fullscreenMode
445  PropertyChanges {
446  target: panelArea;
447  anchors.topMargin: 0
448  opacity: 1;
449  }
450  },
451  State {
452  name: "offscreen" //pushed off screen
453  when: fullscreenMode
454  PropertyChanges {
455  target: panelArea;
456  anchors.topMargin: {
457  if (indicators.state !== "initial") return 0;
458  if (applicationMenus.state !== "initial") return 0;
459  return -minimizedPanelHeight;
460  }
461  opacity: indicators.fullyClosed && applicationMenus.fullyClosed ? 0.0 : 1.0
462  }
463  PropertyChanges {
464  target: indicators.showDragHandle;
465  anchors.bottomMargin: -units.gu(1)
466  }
467  PropertyChanges {
468  target: applicationMenus.showDragHandle;
469  anchors.bottomMargin: -units.gu(1)
470  }
471  }
472  ]
473 
474  transitions: [
475  Transition {
476  to: "onscreen"
477  UbuntuNumberAnimation { target: panelArea; properties: "anchors.topMargin,opacity" }
478  },
479  Transition {
480  to: "offscreen"
481  UbuntuNumberAnimation { target: panelArea; properties: "anchors.topMargin,opacity" }
482  }
483  ]
484 }