Krita Source Code Documentation
Loading...
Searching...
No Matches
KisPopupButton.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2008 Boudewijn Rempt <boud@valdyas.org>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include "KisPopupButton.h"
9
10#include <QPointer>
11#include <QApplication>
12#include <QFrame>
13#include <QHBoxLayout>
14#include <QKeyEvent>
15#include <QScreen>
16#include <QStyleOption>
17#include <QStylePainter>
18#include <QWindow>
19
20#include "kis_global.h"
21#include <kis_debug.h>
22
23
24class KisPopupButtonFrame : public QFrame
25{
26 QHBoxLayout* frameLayout {0};
27
28public:
29 KisPopupButtonFrame(QWidget *parent, bool detach)
30 : QFrame(parent)
31 {
32 setObjectName("KisPopupButtonFrame");
33 setProperty("_kis_excludeFromLayoutThumbnail", true);
34 frameLayout = new QHBoxLayout(this);
35 frameLayout->setContentsMargins(0, 0, 0, 0);
36
37 setDetached(detach);
38 }
39
40 void setDetached(bool detach)
41 {
42#if defined Q_OS_ANDROID || defined Q_OS_MACOS
43 // for some reason when calling destroy() the platform window isn't
44 // hidden first, this corrupts state of the window stack
45 hide();
46#endif
47
48 // Need to destroy the platform window before changing window flags
49 // so that Qt knows to actually apply the new flags...
50 // At least on Windows, not doing this may result in weird window drop
51 // shadows.
52 destroy();
53
54 if (detach) {
55 setWindowFlags(Qt::Dialog);
56 setFrameStyle(QFrame::NoFrame);
57 }
58 else {
59 setWindowFlags(Qt::Popup);
60 setFrameStyle(QFrame::Box | QFrame::Plain);
61 }
62
63 updateGeometry();
64 }
65
66protected:
67 void keyPressEvent(QKeyEvent *event) override
68 {
69 if (event->matches(QKeySequence::Cancel)) {
70 event->accept();
71 hide();
72 } else {
73 QFrame::keyPressEvent(event);
74 }
75 }
76
77 bool event(QEvent *e) override
78 {
79 if (e->type() == QEvent::Close) {
80 e->ignore();
81 hide();
82 return true;
83 }
84 return QFrame::event(e);
85 }
86};
87
88
91 // : frameLayout(nullptr)
92 {}
95 bool arrowVisible { true };
96 bool isPopupDetached { false };
97 bool isDetachedGeometrySet { false };
98};
99
101 : QToolButton(parent)
102 , m_d(new Private)
103{
104 setObjectName("KisPopupButton");
105 connect(this, SIGNAL(released()), SLOT(showPopupWidget()));
106}
107
109{
110 delete m_d->frame;
111 delete m_d;
112}
113
115{
116 m_d->isPopupDetached = detach;
117 if (m_d->frame) {
118 bool wasVisible = isPopupWidgetVisible();
119 m_d->frame->setDetached(detach);
120 if (wasVisible) {
121 // Setting the window flags closes the widget, so make it visible again.
123 if (detach) {
125 }
127 }
128 }
129}
130
132{
133 if (widget) {
134 delete m_d->frame;
135 m_d->frame = new KisPopupButtonFrame(this->window(), m_d->isPopupDetached);
136 m_d->frame->setWindowTitle(widget->windowTitle());
137
138 m_d->popupWidget = widget;
139
140 m_d->frame->layout()->addWidget(m_d->popupWidget);
141 }
142}
143
145{
146 m_d->frame->resize(w, m_d->frame->height());
147}
148
150{
151 if (m_d->popupWidget && !m_d->frame->isVisible()) {
153 } else {
155 }
156}
157
162
164{
165 if (m_d->popupWidget) {
166 if (visible) {
167 // Force layout sizing before positioning
168 m_d->popupWidget->adjustSize();
169 m_d->frame->adjustSize();
171 m_d->frame->raise();
172 m_d->frame->show();
173 m_d->frame->activateWindow();
174 } else {
175 m_d->frame->setVisible(false);
176 }
177 }
178}
179
181{
182 return m_d->popupWidget && m_d->frame->isVisible();
183}
184
185void KisPopupButton::paintEvent ( QPaintEvent * event )
186{
187 QToolButton::paintEvent(event);
188 if (m_d->arrowVisible) {
190 }
191}
192
194{
195 QStylePainter p(this);
196 QStyleOption option;
197 option.rect = QRect(rect().right() - 15, rect().bottom() - 15, 14, 14);
198 option.palette = palette();
199 option.palette.setBrush(QPalette::ButtonText, Qt::black); // Force color to black
200 option.state = QStyle::State_Enabled;
201 p.setBrush(Qt::black); // work around some theme that don't use QPalette::ButtonText like they should, but instead the QPainter brushes and pen
202 p.setPen(Qt::black);
203 p.drawPrimitive(QStyle::PE_IndicatorArrowDown, option);
204}
205
207{
208 // If popup is not detached, or if its detached geometry hasn't been set,
209 // we first move the popup to the "current" screen.
211 QScreen *currentScreen = [this]() {
212 QWindow *mainWinHandle = this->window()->windowHandle();
213 if (mainWinHandle) {
214 return mainWinHandle->screen();
215 }
216 return QApplication::primaryScreen();
217 }();
218 QWindow *winHandle = m_d->frame->windowHandle();
219 if (winHandle) {
220 winHandle->setScreen(currentScreen);
221 }
222 }
223
224 // Attach to the button if it's visible, else attach to the cursor.
225 QPoint pos = this->isVisible() ? this->mapToGlobal(QPoint(0, this->size().height())) : QCursor().pos();
226 QSize popSize = m_d->popupWidget->size();
227 QRect popupRect(pos, popSize);
228
229 // Get the available geometry of the screen which contains the popup.
230 QScreen *screen = [this]() {
231 QWindow *winHandle = m_d->frame->windowHandle();
232 if (winHandle && winHandle->screen()) {
233 return winHandle->screen();
234 }
235 return QApplication::primaryScreen();
236 }();
237 QRect screenRect = screen->availableGeometry();
238 if (m_d->isPopupDetached) {
240 popupRect.moveTo(m_d->frame->geometry().topLeft());
241 } else {
242 popupRect.moveTo(this->window()->geometry().center() - QRect(QPoint(0, 0), popSize).center());
244 }
245 }
246 popupRect = kisEnsureInRect(popupRect, screenRect);
247
248 m_d->frame->setGeometry(popupRect);
249}
250
252{
253 if (v) {
254 m_d->arrowVisible = true;
255 } else {
256 m_d->arrowVisible = false;
257 }
258}
const Params2D p
qreal v
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
bool event(QEvent *e) override
void setDetached(bool detach)
QHBoxLayout * frameLayout
void keyPressEvent(QKeyEvent *event) override
KisPopupButtonFrame(QWidget *parent, bool detach)
void setPopupWidgetVisible(bool visible)
void setPopupWidgetWidth(int w)
void setPopupWidgetDetached(bool detach)
void setPopupWidget(QWidget *widget)
~KisPopupButton() override
Private *const m_d
void setArrowVisible(bool v)
void paintEvent(QPaintEvent *event) override
KisPopupButton(QWidget *parent)
void adjustPosition()
adjustPosition adjusts the position of the popup widget based on the position of this button and the ...
QRect kisEnsureInRect(QRect rc, const QRect &bounds)
Definition kis_global.h:267
rgba palette[MAX_PALETTE]
Definition palette.c:35
QPointer< QWidget > popupWidget
QPointer< KisPopupButtonFrame > frame