Files
pay/guis/mobile/PopupOverlay.qml
T
tomFlowee 55449ffe75 Fix positioning of popups
The popup component adds hidden margins and insets, which we have to
fight to not end up with arbitary positioning.
This fixes the positioning and sizes much better.
2025-02-14 18:44:09 +01:00

149 lines
5.2 KiB
QML

/*
* This file is part of the Flowee project
* Copyright (C) 2022-2025 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls as QQC2
import "../Flowee" as Flowee
FocusScope {
id: root
anchors.fill: parent
enabled: thePopup.visible
property bool isOpen: false;
/**
* @param sourceComponent is a Component we set on the loader.
* @param target is the visual item we position next to (vertically).
* @param overlayComponent is a component that we place on top of the
* \a 'target' item, but inside of the popup to avoid dimming.
* @returns the item instance of the sourceComponent template
*
* Note, make sure that the sourceComponent sets an implicitHeight,
* which is used in the popup.
*/
function open(sourceComponent, target, overlayComponent) {
thePopup.palette = mainWindow.palette
thePopup.width = root.width - 18
thePopup.x = (width - thePopup.width) / 2
if (target === null) {
thePopup.sourceRect = Qt.rect(0, 0, 0, 0);
} else {
thePopup.sourceRect = root.mapFromItem(target, 0, 0, target.width, target.height);
}
overlayLoader.sourceComponent = overlayComponent;
loader.sourceComponent = sourceComponent; // last, as it starts the loading
return loader.item;
}
function close() {
thePopup.visible = false;
}
QQC2.Popup {
id: thePopup
width: parent.width
height: 100
leftInset: -2
rightInset: -2
topInset: 0
bottomInset: 0
modal: true
closePolicy: QQC2.Popup.CloseOnEscape + QQC2.Popup.CloseOnReleaseOutside
property rect sourceRect: Qt.rect(0, 0, 0, 0)
onVisibleChanged: {
if (!visible) { // closing
loader.sourceComponent = undefined;
overlayLoader.sourceComponent = undefined;
}
root.isOpen = visible; // ensure listeners of that property get notified after we acted on visibility changes.
}
background: Rectangle {
color: palette.base
}
Rectangle {
color: palette.light
border.color: palette.midlight
border.width: 1
radius: 5
anchors.fill: loader
anchors.margins: -10 // the popup imposes this border on us, we take it back
}
Loader {
id: loader
width: parent.width
onLoaded: {
thePopup.visible = true;
root.forceActiveFocus();
}
onHeightChanged: {
if (item == null)
return;
var h = loader.item.implicitHeight
thePopup.height = h + 12; // 12 is for top and bottom inset of the popup
var rect = thePopup.sourceRect;
if (overlayLoader.item) { // the overlay is supposed to be at the same position as the sourceRect
var h2 = overlayLoader.height
thePopup.height += h2 + 10;
if (root.height - rect.bottom >= h) { // fits below
thePopup.y = rect.bottom - h2;
overlayLoader.y = -12
loader.y = h2;
}
else if (h < rect.y) { // fits above
thePopup.y = rect.y - h - 22;
overlayLoader.y = h + 10;
loader.y = 0;
}
else {
thePopup.y = root.height - h; // make it bottom aligned, even if it overlaps
overlayLoader.y = 0 // item above
loader.y = h2 - 10;
}
return;
}
loader.y = 0;
if (root.height - rect.bottom >= h) // fits below
thePopup.y = rect.bottom;
else if (h < rect.y) // fits above
thePopup.y = rect.y - h;
else
thePopup.y = root.height - h; // make it bottom aligned, even if it overlaps
}
}
Loader {
// use offsets to counter the popup insets
width: parent.width + 40
x: -20
id: overlayLoader
}
}
Keys.onPressed: (event)=> {
if (event.key === Qt.Key_Back || event.key === Qt.Key_Escape) {
event.accepted = true;
root.close();
}
}
}