2 * Copyright (C) 2013-2017 Canonical Ltd.
3 * Copyright (C) 2020-2026 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
47 property var orientation: Qt.PrimaryOrientation
49 property alias applicationMenus: __applicationMenus
50 property alias indicators: __indicators
51 property bool fullscreenMode: false
52 property real panelAreaShowProgress: 1.0
53 property bool greeterShown: false
54 property bool hasKeyboard: false
55 property bool supportsMultiColorLed: true
56 property var blurSource : null
58 // Whether our expanded menus should take up the full width of the panel
59 property bool partialWidth: width >= units.gu(60)
61 property string mode: "staged"
62 property PanelState panelState
64 property bool temporarilyShown: false
66 function temporarilyShow() {
67 temporarilyShown = true
68 temporaryShowTimeout.restart()
72 id: temporaryShowTimeout
76 temporarilyShown = false
83 anchors.topMargin: panelHeight
84 visible: !indicators.fullyClosed || !applicationMenus.fullyClosed
86 hoverEnabled: true // should also eat hover events, otherwise they will pass through
89 __applicationMenus.hide();
96 restoreMode: Binding.RestoreBinding
97 property: "panelHeight"
98 value: minimizedPanelHeight
101 RegisteredApplicationMenuModel {
102 id: registeredMenuModel
103 persistentSurfaceId: panelState.focusedPersistentSurfaceId
109 property bool revealControls: !greeterShown &&
110 !applicationMenus.shown &&
112 (decorationMouseArea.containsMouse || menuBarLoader.menusRequested)
114 property bool showWindowDecorationControls: (revealControls && panelState.decorationsVisible) ||
115 panelState.decorationsAlwaysVisible
117 property bool showPointerMenu: revealControls &&
118 (panelState.decorationsVisible || mode == "windowed")
120 property bool enablePointerMenu: applicationMenus.available &&
121 applicationMenus.model
123 property bool showTouchMenu: !greeterShown &&
125 !showWindowDecorationControls
127 property bool enableTouchMenus: showTouchMenu &&
128 applicationMenus.available &&
129 applicationMenus.model
134 objectName: "panelArea"
138 transform: Translate {
139 y: indicators.state === "initial"
140 ? (1.0 - panelAreaShowProgress) * - minimizedPanelHeight
145 id: indicatorsDropShadow
148 margins: -units.gu(1)
150 visible: !__indicators.fullyClosed
151 source: "graphics/rectangular_dropshadow.sci"
155 id: appmenuDropShadow
157 fill: __applicationMenus
158 margins: -units.gu(1)
160 visible: !__applicationMenus.fullyClosed
161 source: "graphics/rectangular_dropshadow.sci"
167 fill: panelAreaBackground
168 bottomMargin: -units.gu(1)
170 visible: panelState.dropShadow
171 source: "graphics/rectangular_dropshadow.sci"
175 id: panelAreaBackground
176 color: callHint.visible ? theme.palette.normal.activity :
177 Qt.hsla(0, 0, Math.round(theme.palette.normal.background.hslLightness), 1)
183 height: minimizedPanelHeight
185 Behavior on color { ColorAnimation { duration: LomiriAnimation.FastDuration } }
189 id: decorationMouseArea
190 objectName: "windowControlArea"
195 height: minimizedPanelHeight
196 hoverEnabled: !__indicators.shown
198 if (callHint.visible) {
199 callHint.showLiveCall();
204 if (!callHint.visible) {
205 // let it fall through to the window decoration of the maximized window behind, if any
206 mouse.accepted = false;
208 var menubar = menuBarLoader.item;
210 menubar.invokeMenu(mouse);
218 // WindowControlButtons inside the mouse area, otherwise QML doesn't grok nested hover events :/
219 // cf. https://bugreports.qt.io/browse/QTBUG-32909
220 WindowControlButtons {
221 id: windowControlButtons
222 objectName: "panelWindowControlButtons"
223 height: indicators.minimizedPanelHeight
224 opacity: d.showWindowDecorationControls ? 1 : 0
225 visible: opacity != 0
226 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
228 active: panelState.decorationsVisible || panelState.decorationsAlwaysVisible
229 windowIsMaximized: true
230 onCloseClicked: panelState.closeClicked()
231 onMinimizeClicked: panelState.minimizeClicked()
232 onMaximizeClicked: panelState.restoreClicked()
233 closeButtonShown: panelState.closeButtonShown
238 objectName: "menuBarLoader"
239 height: parent.height
240 enabled: d.enablePointerMenu
241 opacity: d.showPointerMenu ? 1 : 0
242 visible: opacity != 0
243 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
244 active: d.showPointerMenu && !callHint.visible
246 width: parent.width - windowControlButtons.width - units.gu(2) - __indicators.barWidth
248 readonly property bool menusRequested: menuBarLoader.item ? menuBarLoader.item.showRequested : false
250 sourceComponent: MenuBar {
252 objectName: "menuBar"
253 anchors.left: menuBarLoader ? menuBarLoader.left : undefined
254 anchors.margins: units.gu(1)
255 height: menuBarLoader.height
256 enableKeyFilter: valid && panelState.decorationsVisible
257 lomiriMenuModel: __applicationMenus.model
258 panelState: root.panelState
261 target: __applicationMenus
262 function onShownChanged() { bar.dismiss(); }
267 function onShownChanged() { bar.dismiss(); }
270 onDoubleClicked: panelState.restoreClicked()
271 onPressed: mouse.accepted = false // let the parent mouse area handle this, so it can both unsnap window and show menu
278 objectName: "callHint"
280 anchors.centerIn: parent
281 height: minimizedPanelHeight
283 visible: active && indicators.state == "initial" && __applicationMenus.state == "initial"
284 greeterShown: root.greeterShown
289 id: __applicationMenus
292 model: registeredMenuModel.model
293 width: root.menuWidth
295 minimizedPanelHeight: root.minimizedPanelHeight
296 expandedPanelHeight: root.expandedPanelHeight
297 openedHeight: root.height
298 alignment: Qt.AlignLeft
299 enableHint: !callHint.active && !fullscreenMode
301 panelColor: panelAreaBackground.color
302 blurSource: root.blurSource
307 screenIndex: root.screenIndex
310 if (callHint.active) {
311 callHint.showLiveCall();
316 rowItemDelegate: ActionItem {
318 property int ownIndex: index
319 objectName: "appMenuItem"+index
320 enabled: model.sensitive
322 width: _title.width + units.gu(2)
323 height: parent.height
326 text: model.label.replace("_", "&")
331 anchors.centerIn: parent
332 text: actionItem.text
333 horizontalAlignment: Text.AlignLeft
334 color: enabled ? theme.palette.normal.backgroundText : theme.palette.disabled.backgroundText
338 pageDelegate: PanelMenuPage {
339 readonly property bool isCurrent: modelIndex == __applicationMenus.currentMenuIndex
340 onIsCurrentChanged: {
341 if (isCurrent && menuModel) {
342 menuModel.aboutToShow(modelIndex);
346 menuModel: __applicationMenus.model
347 submenuIndex: modelIndex
349 factory: ApplicationMenuItemFactory {
350 rootModel: __applicationMenus.model
354 enabled: d.enableTouchMenus
355 opacity: d.showTouchMenu ? 1 : 0
356 visible: opacity != 0
358 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
361 if (!enabled) hide();
369 leftMargin: units.gu(1)
370 right: __indicators.left
371 rightMargin: units.gu(1)
373 height: root.minimizedPanelHeight
379 right: root.partialWidth ? parent.right : parent.left
380 rightMargin: touchMenuIcon.width
382 objectName: "panelTitle"
383 height: root.minimizedPanelHeight
384 verticalAlignment: Text.AlignVCenter
385 elide: Text.ElideRight
388 font.weight: Font.Medium
389 color: theme.palette.selected.backgroundText
390 text: (root.partialWidth && !callHint.visible) ? panelState.title : ""
391 opacity: __applicationMenus.visible && !__applicationMenus.expanded
392 Behavior on opacity { NumberAnimation { duration: LomiriAnimation.SnapDuration } }
393 visible: opacity !== 0
398 objectName: "touchMenuIcon"
401 leftMargin: rowLabel.contentWidth + units.dp(2)
402 verticalCenter: parent.verticalCenter
407 color: theme.palette.normal.backgroundText
408 opacity: !__applicationMenus.expanded && d.enableTouchMenus && !callHint.visible
409 Behavior on opacity { NumberAnimation { duration: LomiriAnimation.SnapDuration } }
410 visible: opacity !== 0
416 objectName: "indicators"
422 width: root.menuWidth
423 minimizedPanelHeight: root.minimizedPanelHeight
424 expandedPanelHeight: root.expandedPanelHeight
425 openedHeight: root.height
426 screenIndex: root.screenIndex
427 orientation: root.orientation
429 overFlowWidth: width - appMenuClear
430 enableHint: !callHint.active && !fullscreenMode
431 showOnClick: !callHint.visible
432 panelColor: panelAreaBackground.color
433 blurSource: root.blurSource
439 // On small screens, the Indicators' handle area is the entire top
440 // bar unless there is an application menu. In that case, our handle
441 // needs to allow for some room to clear the application menu.
442 property var appMenuClear: (d.enableTouchMenus && !partialWidth) ? units.gu(7) : 0
445 if (callHint.active) {
446 callHint.showLiveCall();
450 rowItemDelegate: IndicatorItem {
452 objectName: identifier+"-panelItem"
454 property int ownIndex: index
455 // FIXME: there was logic here to not clash with app menus without clip: true, bring it back with app menus
456 readonly property bool hidden: !expanded && (!indicatorVisible || hideSessionIndicator || hideKeyboardIndicator)
457 // HACK for indicator-session
458 readonly property bool hideSessionIndicator: identifier == "ayatana-indicator-session" && Math.min(Screen.width, Screen.height) <= units.gu(60)
459 // HACK for indicator-keyboard
460 readonly property bool hideKeyboardIndicator: identifier == "ayatana-indicator-keyboard" && !hasKeyboard
462 height: parent.height
463 expanded: indicators.expanded
464 finishedExpanding: Math.floor(height) == Math.floor(__indicators.expandedPanelHeight)
465 selected: index == indicators.currentMenuIndex
467 identifier: model.identifier
468 busName: indicatorProperties.busName
469 actionsObjectPath: indicatorProperties.actionsObjectPath
470 menuObjectPath: indicatorProperties.menuObjectPath
472 opacity: hidden ? 0.0 : 1.0
473 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
475 width: ((expanded || indicatorVisible) && !hideSessionIndicator && !hideKeyboardIndicator) ? implicitWidth : 0
477 Behavior on width { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
480 pageDelegate: PanelMenuPage {
481 objectName: modelData.identifier + "-page"
484 menuModel: delegate.menuModel
486 factory: IndicatorMenuItemFactory {
488 var context = modelData.identifier;
489 if (context && context.indexOf("fake-") === 0) {
490 context = context.substring("fake-".length)
494 rootModel: delegate.menuModel
499 busName: modelData.indicatorProperties.busName
500 actionsObjectPath: modelData.indicatorProperties.actionsObjectPath
501 menuObjectPath: modelData.indicatorProperties.menuObjectPath
505 enabled: !applicationMenus.expanded
506 opacity: !callHint.visible && !applicationMenus.expanded ? 1 : 0
508 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
511 if (!enabled) hide();
518 supportsMultiColorLed: root.supportsMultiColorLed
523 name: "onscreen" //fully opaque and visible at top edge of screen
524 when: !fullscreenMode || temporarilyShown
532 name: "offscreen" //pushed off screen
537 if (indicators.state !== "initial") return 0;
538 if (applicationMenus.state !== "initial") return 0;
539 return -minimizedPanelHeight;
541 opacity: indicators.fullyClosed && applicationMenus.fullyClosed ? 0.0 : 1.0
544 target: indicators.showDragHandle;
545 anchors.bottomMargin: -units.gu(1)
548 target: applicationMenus.showDragHandle;
549 anchors.bottomMargin: -units.gu(1)
557 LomiriNumberAnimation { target: panelArea; properties: "anchors.topMargin,opacity" }
561 LomiriNumberAnimation { target: panelArea; properties: "anchors.topMargin,opacity" }