File indexing completed on 2024-04-28 15:32:11

0001 /*  -*- C++ -*-
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1997 Tim D. Gilman <tdgilman@best.org>
0004     SPDX-FileCopyrightText: 1998-2001 Mirko Boehm <mirko@kde.org>
0005     SPDX-FileCopyrightText: 2007 John Layt <john@layt.net>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kpopupframe.h"
0011 
0012 #include <QEventLoop>
0013 #include <QGuiApplication>
0014 #include <QKeyEvent>
0015 #include <QScreen>
0016 
0017 class KPopupFramePrivate
0018 {
0019 public:
0020     KPopupFramePrivate(KPopupFrame *qq);
0021     ~KPopupFramePrivate();
0022 
0023     KPopupFrame *q;
0024 
0025     /**
0026      * The result. It is returned from exec() when the popup window closes.
0027      */
0028     int result;
0029 
0030     /**
0031      * The only subwidget that uses the whole dialog window.
0032      */
0033     QWidget *main;
0034 
0035     // TODO KF6: Remove this, add a hideEvent() reimplementation instead.
0036     class OutsideClickCatcher;
0037     OutsideClickCatcher *outsideClickCatcher;
0038 };
0039 
0040 class KPopupFramePrivate::OutsideClickCatcher : public QObject
0041 {
0042     Q_OBJECT
0043 public:
0044     explicit OutsideClickCatcher(QObject *parent = nullptr)
0045         : QObject(parent)
0046         , m_popup(nullptr)
0047     {
0048     }
0049     ~OutsideClickCatcher() override
0050     {
0051     }
0052 
0053     void setPopupFrame(KPopupFrame *popup)
0054     {
0055         m_popup = popup;
0056         popup->installEventFilter(this);
0057     }
0058 
0059     KPopupFrame *m_popup;
0060 
0061     bool eventFilter(QObject *object, QEvent *event) override
0062     {
0063         Q_UNUSED(object);
0064 
0065         // To catch outside clicks, it is sufficient to check for
0066         // hide events on Qt::Popup type widgets
0067         if (event->type() == QEvent::Hide && m_popup) {
0068             // do not set d->result here, because the popup
0069             // hides itself after leaving the event loop.
0070             Q_EMIT m_popup->leaveModality();
0071         }
0072         return false;
0073     }
0074 };
0075 
0076 KPopupFramePrivate::KPopupFramePrivate(KPopupFrame *qq)
0077     : q(qq)
0078     , result(0)
0079     , // rejected
0080     main(nullptr)
0081     , outsideClickCatcher(new OutsideClickCatcher)
0082 {
0083     outsideClickCatcher->setPopupFrame(q);
0084 }
0085 
0086 KPopupFramePrivate::~KPopupFramePrivate()
0087 {
0088     delete outsideClickCatcher;
0089 }
0090 
0091 KPopupFrame::KPopupFrame(QWidget *parent)
0092     : QFrame(parent, Qt::Popup)
0093     , d(new KPopupFramePrivate(this))
0094 {
0095     setFrameStyle(QFrame::Box | QFrame::Raised);
0096     setMidLineWidth(2);
0097 }
0098 
0099 KPopupFrame::~KPopupFrame() = default;
0100 
0101 void KPopupFrame::keyPressEvent(QKeyEvent *e)
0102 {
0103     if (e->key() == Qt::Key_Escape) {
0104         d->result = 0; // rejected
0105         Q_EMIT leaveModality();
0106         // qApp->exit_loop();
0107     }
0108 }
0109 
0110 void KPopupFrame::hideEvent(QHideEvent *e)
0111 {
0112     QFrame::hideEvent(e);
0113 }
0114 
0115 void KPopupFrame::close(int r)
0116 {
0117     d->result = r;
0118     Q_EMIT leaveModality();
0119     // qApp->exit_loop();
0120 }
0121 
0122 void KPopupFrame::setMainWidget(QWidget *m)
0123 {
0124     d->main = m;
0125     if (d->main) {
0126         resize(d->main->width() + 2 * frameWidth(), d->main->height() + 2 * frameWidth());
0127     }
0128 }
0129 
0130 void KPopupFrame::resizeEvent(QResizeEvent *e)
0131 {
0132     Q_UNUSED(e);
0133 
0134     if (d->main) {
0135         d->main->setGeometry(frameWidth(),
0136                              frameWidth(), //
0137                              width() - 2 * frameWidth(), //
0138                              height() - 2 * frameWidth());
0139     }
0140 }
0141 
0142 void KPopupFrame::popup(const QPoint &pos)
0143 {
0144     // Make sure the whole popup is visible.
0145     QScreen *screen = QGuiApplication::screenAt(pos);
0146 
0147     int x = pos.x();
0148     int y = pos.y();
0149     int w = width();
0150     int h = height();
0151     if (screen) {
0152         const QRect desktopGeometry = screen->geometry();
0153         if (x + w > desktopGeometry.x() + desktopGeometry.width()) {
0154             x = desktopGeometry.width() - w;
0155         }
0156         if (y + h > desktopGeometry.y() + desktopGeometry.height()) {
0157             y = desktopGeometry.height() - h;
0158         }
0159         if (x < desktopGeometry.x()) {
0160             x = 0;
0161         }
0162         if (y < desktopGeometry.y()) {
0163             y = 0;
0164         }
0165     }
0166 
0167     // Pop the thingy up.
0168     move(x, y);
0169     show();
0170     d->main->setFocus();
0171 }
0172 
0173 int KPopupFrame::exec(const QPoint &pos)
0174 {
0175     popup(pos);
0176     repaint();
0177     d->result = 0; // rejected
0178     QEventLoop eventLoop;
0179     connect(this, &KPopupFrame::leaveModality, &eventLoop, &QEventLoop::quit);
0180     eventLoop.exec();
0181 
0182     hide();
0183     return d->result;
0184 }
0185 
0186 int KPopupFrame::exec(int x, int y)
0187 {
0188     return exec(QPoint(x, y));
0189 }
0190 
0191 #include "kpopupframe.moc"
0192 #include "moc_kpopupframe.cpp"