File indexing completed on 2025-01-19 03:50:33
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 1997-04-21 0007 * Description : Frame with popup menu behavior. 0008 * 0009 * SPDX-FileCopyrightText: 2011-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 1997 by Tim D. Gilman <tdgilman at best dot org> 0011 * SPDX-FileCopyrightText: 1998-2001 by Mirko Boehm <mirko at kde dot org> 0012 * SPDX-FileCopyrightText: 2007 by John Layt <john at layt dot net> 0013 * 0014 * SPDX-License-Identifier: GPL-2.0-or-later 0015 * 0016 * ============================================================ */ 0017 0018 #include "dpopupframe.h" 0019 0020 // Qt includes 0021 0022 #include <QApplication> 0023 #include <QEventLoop> 0024 #include <QKeyEvent> 0025 #include <QScreen> 0026 #include <QWindow> 0027 0028 namespace Digikam 0029 { 0030 0031 class Q_DECL_HIDDEN DPopupFrame::Private 0032 { 0033 public: 0034 0035 explicit Private(DPopupFrame* const qq); 0036 ~Private(); 0037 0038 public: 0039 0040 DPopupFrame* q; 0041 0042 /** 0043 * The result. It is returned from exec() when the popup window closes. 0044 */ 0045 int result; 0046 0047 /** 0048 * The only subwidget that uses the whole dialog window. 0049 */ 0050 QWidget* main; 0051 0052 class OutsideClickCatcher; 0053 OutsideClickCatcher* outsideClickCatcher; 0054 }; 0055 0056 class Q_DECL_HIDDEN DPopupFrame::Private::OutsideClickCatcher : public QObject 0057 { 0058 Q_OBJECT 0059 0060 public: 0061 0062 explicit OutsideClickCatcher(QObject* const parent = nullptr) 0063 : QObject(parent), 0064 m_popup(nullptr) 0065 { 0066 } 0067 0068 ~OutsideClickCatcher() override 0069 { 0070 } 0071 0072 void setPopupFrame(DPopupFrame* const popup) 0073 { 0074 m_popup = popup; 0075 popup->installEventFilter(this); 0076 } 0077 0078 0079 bool eventFilter(QObject* object, QEvent* event) override 0080 { 0081 Q_UNUSED(object); 0082 0083 // To catch outside clicks, it is sufficient to check for 0084 // hide events on Qt::Popup type widgets 0085 0086 if (event->type() == QEvent::Hide && m_popup) 0087 { 0088 // do not set d->result here, because the popup 0089 // hides itself after leaving the event loop. 0090 0091 Q_EMIT m_popup->leaveModality(); 0092 } 0093 0094 return false; 0095 } 0096 0097 public: 0098 0099 DPopupFrame* m_popup; 0100 }; 0101 0102 DPopupFrame::Private::Private(DPopupFrame* const qq) 0103 : q (qq), 0104 result (0), // rejected 0105 main (nullptr), 0106 outsideClickCatcher(new OutsideClickCatcher) 0107 { 0108 outsideClickCatcher->setPopupFrame(q); 0109 } 0110 0111 DPopupFrame::Private::~Private() 0112 { 0113 delete outsideClickCatcher; 0114 } 0115 0116 DPopupFrame::DPopupFrame(QWidget* const parent) 0117 : QFrame(parent, Qt::Popup), 0118 d(new Private(this)) 0119 { 0120 setFrameStyle(QFrame::Box | QFrame::Raised); 0121 setMidLineWidth(2); 0122 } 0123 0124 DPopupFrame::~DPopupFrame() 0125 { 0126 delete d; 0127 } 0128 0129 void DPopupFrame::keyPressEvent(QKeyEvent* e) 0130 { 0131 if (e->key() == Qt::Key_Escape) 0132 { 0133 d->result = 0; // rejected 0134 Q_EMIT leaveModality(); 0135 } 0136 } 0137 0138 void DPopupFrame::hideEvent(QHideEvent *e) 0139 { 0140 QFrame::hideEvent(e); 0141 } 0142 0143 void DPopupFrame::close(int r) 0144 { 0145 d->result = r; 0146 0147 Q_EMIT leaveModality(); 0148 } 0149 0150 void DPopupFrame::setMainWidget(QWidget* const m) 0151 { 0152 d->main = m; 0153 0154 if (d->main) 0155 { 0156 resize(d->main->width() + 2 * frameWidth(), d->main->height() + 2 * frameWidth()); 0157 } 0158 } 0159 0160 void DPopupFrame::resizeEvent(QResizeEvent* e) 0161 { 0162 Q_UNUSED(e); 0163 0164 if (d->main) 0165 { 0166 d->main->setGeometry(frameWidth(), frameWidth(), 0167 width() - 2 * frameWidth(), height() - 2 * frameWidth()); 0168 } 0169 } 0170 0171 void DPopupFrame::popup(const QPoint& p) 0172 { 0173 // Make sure the whole popup is visible. 0174 0175 QScreen* screen = qApp->primaryScreen(); 0176 0177 if (QWidget* const widget = nativeParentWidget()) 0178 { 0179 if (QWindow* const window = widget->windowHandle()) 0180 { 0181 screen = window->screen(); 0182 } 0183 } 0184 0185 QRect desktopGeometry = screen->geometry(); 0186 0187 int x = p.x(); 0188 int y = p.y(); 0189 int w = width(); 0190 int h = height(); 0191 0192 if ((x + w) > (desktopGeometry.x() + desktopGeometry.width())) 0193 { 0194 x = desktopGeometry.width() - w; 0195 } 0196 0197 if ((y + h) > (desktopGeometry.y() + desktopGeometry.height())) 0198 { 0199 y = desktopGeometry.height() - h; 0200 } 0201 0202 if (x < desktopGeometry.x()) 0203 { 0204 x = 0; 0205 } 0206 0207 if (y < desktopGeometry.y()) 0208 { 0209 y = 0; 0210 } 0211 0212 // Pop the thingy up. 0213 0214 move(x, y); 0215 show(); 0216 d->main->setFocus(); 0217 } 0218 0219 int DPopupFrame::exec(const QPoint& p) 0220 { 0221 popup(p); 0222 repaint(); 0223 d->result = 0; // rejected 0224 QEventLoop eventLoop; 0225 0226 connect(this, &DPopupFrame::leaveModality, 0227 &eventLoop, &QEventLoop::quit); 0228 0229 eventLoop.exec(); 0230 0231 hide(); 0232 0233 return d->result; 0234 } 0235 0236 int DPopupFrame::exec(int x, int y) 0237 { 0238 return exec(QPoint(x, y)); 0239 } 0240 0241 } // namespace Digikam 0242 0243 #include "dpopupframe.moc" 0244 0245 #include "moc_dpopupframe.cpp"