2 * Copyright (C) 2013-2017 Canonical Ltd.
3 * Copyright (C) 2020 UBports Foundation
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20import Lomiri.Components 1.3
21import Lomiri.Layouts 1.0
22import QtMir.Application 0.1
23import Lomiri.Indicators 0.1
25import Lomiri.ApplicationMenu 0.1
27import QtQuick.Window 2.2
29import "../../../ApplicationMenus"
30import "../../../Components"
31import "../../../Components/PanelState"
33import "../../Indicators"
39 readonly property real panelHeight: panelArea.y + minimizedPanelHeight
40 readonly property bool fullyClosed: indicators.fullyClosed && applicationMenus.fullyClosed
42 property real minimizedPanelHeight: units.gu(3)
43 property real expandedPanelHeight: units.gu(7)
44 property real menuWidth: partialWidth ? units.gu(40) : width
45 property alias applicationMenuContentX: __applicationMenus.menuContentX
46 property int screenIndex: -1
48 property alias applicationMenus: __applicationMenus
49 property alias indicators: __indicators
50 property bool fullscreenMode: false
51 property real panelAreaShowProgress: 1.0
52 property bool greeterShown: false
53 property bool hasKeyboard: false
54 property bool supportsMultiColorLed: true
55 property var blurSource : null
57 // Whether our expanded menus should take up the full width of the panel
58 property bool partialWidth: width >= units.gu(60)
60 property string mode: "staged"
61 property PanelState panelState
63 property bool temporarilyShown: false
65 function temporarilyShow() {
66 temporarilyShown = true
67 temporaryShowTimeout.restart()
71 id: temporaryShowTimeout
75 temporarilyShown = false
82 anchors.topMargin: panelHeight
83 visible: !indicators.fullyClosed || !applicationMenus.fullyClosed
85 hoverEnabled: true // should also eat hover events, otherwise they will pass through
88 __applicationMenus.hide();
95 restoreMode: Binding.RestoreBinding
96 property: "panelHeight"
97 value: minimizedPanelHeight
100 RegisteredApplicationMenuModel {
101 id: registeredMenuModel
102 persistentSurfaceId: panelState.focusedPersistentSurfaceId
108 property bool revealControls: !greeterShown &&
109 !applicationMenus.shown &&
111 (decorationMouseArea.containsMouse || menuBarLoader.menusRequested)
113 property bool showWindowDecorationControls: (revealControls && panelState.decorationsVisible) ||
114 panelState.decorationsAlwaysVisible
116 property bool showPointerMenu: revealControls &&
117 (panelState.decorationsVisible || mode == "windowed")
119 property bool enablePointerMenu: applicationMenus.available &&
120 applicationMenus.model
122 property bool showTouchMenu: !greeterShown &&
124 !showWindowDecorationControls
126 property bool enableTouchMenus: showTouchMenu &&
127 applicationMenus.available &&
128 applicationMenus.model
133 objectName: "panelArea"
137 transform: Translate {
138 y: indicators.state === "initial"
139 ? (1.0 - panelAreaShowProgress) * - minimizedPanelHeight
144 id: indicatorsDropShadow
147 margins: -units.gu(1)
149 visible: !__indicators.fullyClosed
150 source: "graphics/rectangular_dropshadow.sci"
154 id: appmenuDropShadow
156 fill: __applicationMenus
157 margins: -units.gu(1)
159 visible: !__applicationMenus.fullyClosed
160 source: "graphics/rectangular_dropshadow.sci"
166 fill: panelAreaBackground
167 bottomMargin: -units.gu(1)
169 visible: panelState.dropShadow
170 source: "graphics/rectangular_dropshadow.sci"
174 id: panelAreaBackground
175 color: callHint.visible ? theme.palette.normal.activity :
176 Qt.hsla(0, 0, Math.round(theme.palette.normal.background.hslLightness), 1)
182 height: minimizedPanelHeight
184 Behavior on color { ColorAnimation { duration: LomiriAnimation.FastDuration } }
188 id: decorationMouseArea
189 objectName: "windowControlArea"
194 height: minimizedPanelHeight
195 hoverEnabled: !__indicators.shown
197 if (callHint.visible) {
198 callHint.showLiveCall();
203 if (!callHint.visible) {
204 // let it fall through to the window decoration of the maximized window behind, if any
205 mouse.accepted = false;
207 var menubar = menuBarLoader.item;
209 menubar.invokeMenu(mouse);
217 // WindowControlButtons inside the mouse area, otherwise QML doesn't grok nested hover events :/
218 // cf. https://bugreports.qt.io/browse/QTBUG-32909
219 WindowControlButtons {
220 id: windowControlButtons
221 objectName: "panelWindowControlButtons"
222 height: indicators.minimizedPanelHeight
223 opacity: d.showWindowDecorationControls ? 1 : 0
224 visible: opacity != 0
225 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
227 active: panelState.decorationsVisible || panelState.decorationsAlwaysVisible
228 windowIsMaximized: true
229 onCloseClicked: panelState.closeClicked()
230 onMinimizeClicked: panelState.minimizeClicked()
231 onMaximizeClicked: panelState.restoreClicked()
232 closeButtonShown: panelState.closeButtonShown
237 objectName: "menuBarLoader"
238 height: parent.height
239 enabled: d.enablePointerMenu
240 opacity: d.showPointerMenu ? 1 : 0
241 visible: opacity != 0
242 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
243 active: d.showPointerMenu && !callHint.visible
245 width: parent.width - windowControlButtons.width - units.gu(2) - __indicators.barWidth
247 readonly property bool menusRequested: menuBarLoader.item ? menuBarLoader.item.showRequested : false
249 sourceComponent: MenuBar {
251 objectName: "menuBar"
252 anchors.left: menuBarLoader ? menuBarLoader.left : undefined
253 anchors.margins: units.gu(1)
254 height: menuBarLoader.height
255 enableKeyFilter: valid && panelState.decorationsVisible
256 lomiriMenuModel: __applicationMenus.model
257 panelState: root.panelState
260 target: __applicationMenus
261 function onShownChanged() { bar.dismiss(); }
266 function onShownChanged() { bar.dismiss(); }
269 onDoubleClicked: panelState.restoreClicked()
270 onPressed: mouse.accepted = false // let the parent mouse area handle this, so it can both unsnap window and show menu
277 objectName: "callHint"
279 anchors.centerIn: parent
280 height: minimizedPanelHeight
282 visible: active && indicators.state == "initial" && __applicationMenus.state == "initial"
283 greeterShown: root.greeterShown
288 id: __applicationMenus
291 model: registeredMenuModel.model
292 width: root.menuWidth
294 minimizedPanelHeight: root.minimizedPanelHeight
295 expandedPanelHeight: root.expandedPanelHeight
296 openedHeight: root.height
297 alignment: Qt.AlignLeft
298 enableHint: !callHint.active && !fullscreenMode
300 panelColor: panelAreaBackground.color
301 blurSource: root.blurSource
308 if (callHint.active) {
309 callHint.showLiveCall();
314 rowItemDelegate: ActionItem {
316 property int ownIndex: index
317 objectName: "appMenuItem"+index
318 enabled: model.sensitive
320 width: _title.width + units.gu(2)
321 height: parent.height
324 text: model.label.replace("_", "&")
329 anchors.centerIn: parent
330 text: actionItem.text
331 horizontalAlignment: Text.AlignLeft
332 color: enabled ? theme.palette.normal.backgroundText : theme.palette.disabled.backgroundText
336 pageDelegate: PanelMenuPage {
337 readonly property bool isCurrent: modelIndex == __applicationMenus.currentMenuIndex
338 onIsCurrentChanged: {
339 if (isCurrent && menuModel) {
340 menuModel.aboutToShow(modelIndex);
344 menuModel: __applicationMenus.model
345 submenuIndex: modelIndex
347 factory: ApplicationMenuItemFactory {
348 rootModel: __applicationMenus.model
352 enabled: d.enableTouchMenus
353 opacity: d.showTouchMenu ? 1 : 0
354 visible: opacity != 0
356 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
359 if (!enabled) hide();
367 leftMargin: units.gu(1)
368 right: __indicators.left
369 rightMargin: units.gu(1)
371 height: root.minimizedPanelHeight
377 right: root.partialWidth ? parent.right : parent.left
378 rightMargin: touchMenuIcon.width
380 objectName: "panelTitle"
381 height: root.minimizedPanelHeight
382 verticalAlignment: Text.AlignVCenter
383 elide: Text.ElideRight
386 font.weight: Font.Medium
387 color: theme.palette.selected.backgroundText
388 text: (root.partialWidth && !callHint.visible) ? panelState.title : ""
389 opacity: __applicationMenus.visible && !__applicationMenus.expanded
390 Behavior on opacity { NumberAnimation { duration: LomiriAnimation.SnapDuration } }
391 visible: opacity !== 0
396 objectName: "touchMenuIcon"
399 leftMargin: rowLabel.contentWidth + units.dp(2)
400 verticalCenter: parent.verticalCenter
405 color: theme.palette.normal.backgroundText
406 opacity: !__applicationMenus.expanded && d.enableTouchMenus && !callHint.visible
407 Behavior on opacity { NumberAnimation { duration: LomiriAnimation.SnapDuration } }
408 visible: opacity !== 0
414 objectName: "indicators"
420 width: root.menuWidth
421 minimizedPanelHeight: root.minimizedPanelHeight
422 expandedPanelHeight: root.expandedPanelHeight
423 openedHeight: root.height
425 overFlowWidth: width - appMenuClear
426 enableHint: !callHint.active && !fullscreenMode
427 showOnClick: !callHint.visible
428 panelColor: panelAreaBackground.color
429 blurSource: root.blurSource
435 // On small screens, the Indicators' handle area is the entire top
436 // bar unless there is an application menu. In that case, our handle
437 // needs to allow for some room to clear the application menu.
438 property var appMenuClear: (d.enableTouchMenus && !partialWidth) ? units.gu(7) : 0
441 if (callHint.active) {
442 callHint.showLiveCall();
446 rowItemDelegate: IndicatorItem {
448 objectName: identifier+"-panelItem"
450 property int ownIndex: index
451 readonly property bool overflow: parent.width - (x - __indicators.rowContentX) > __indicators.overFlowWidth
452 readonly property bool hidden: !expanded && (overflow || !indicatorVisible || hideSessionIndicator || hideKeyboardIndicator)
453 // HACK for indicator-session
454 readonly property bool hideSessionIndicator: identifier == "ayatana-indicator-session" && Math.min(Screen.width, Screen.height) <= units.gu(60)
455 // HACK for indicator-keyboard
456 readonly property bool hideKeyboardIndicator: identifier == "ayatana-indicator-keyboard" && !hasKeyboard
458 height: parent.height
459 expanded: indicators.expanded
460 selected: ListView.isCurrentItem
462 identifier: model.identifier
463 busName: indicatorProperties.busName
464 actionsObjectPath: indicatorProperties.actionsObjectPath
465 menuObjectPath: indicatorProperties.menuObjectPath
467 opacity: hidden ? 0.0 : 1.0
468 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
470 width: ((expanded || indicatorVisible) && !hideSessionIndicator && !hideKeyboardIndicator) ? implicitWidth : 0
472 Behavior on width { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
475 pageDelegate: PanelMenuPage {
476 objectName: modelData.identifier + "-page"
479 menuModel: delegate.menuModel
481 factory: IndicatorMenuItemFactory {
483 var context = modelData.identifier;
484 if (context && context.indexOf("fake-") === 0) {
485 context = context.substring("fake-".length)
489 rootModel: delegate.menuModel
494 busName: modelData.indicatorProperties.busName
495 actionsObjectPath: modelData.indicatorProperties.actionsObjectPath
496 menuObjectPath: modelData.indicatorProperties.menuObjectPath
500 enabled: !applicationMenus.expanded
501 opacity: !callHint.visible && !applicationMenus.expanded ? 1 : 0
503 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
506 if (!enabled) hide();
513 supportsMultiColorLed: root.supportsMultiColorLed
518 name: "onscreen" //fully opaque and visible at top edge of screen
519 when: !fullscreenMode || temporarilyShown
527 name: "offscreen" //pushed off screen
532 if (indicators.state !== "initial") return 0;
533 if (applicationMenus.state !== "initial") return 0;
534 return -minimizedPanelHeight;
536 opacity: indicators.fullyClosed && applicationMenus.fullyClosed ? 0.0 : 1.0
539 target: indicators.showDragHandle;
540 anchors.bottomMargin: -units.gu(1)
543 target: applicationMenus.showDragHandle;
544 anchors.bottomMargin: -units.gu(1)
552 LomiriNumberAnimation { target: panelArea; properties: "anchors.topMargin,opacity" }
556 LomiriNumberAnimation { target: panelArea; properties: "anchors.topMargin,opacity" }