/* * This file is part of the Flowee project * Copyright (C) 2022-2025 Tom Zander * * 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 . */ 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(); } } }