2 * This file is part of the PulseView project.
4 * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
24 #include <QApplication>
25 #include <QDesktopWidget>
36 const unsigned int Popup::ArrowLength
= 10;
37 const unsigned int Popup::ArrowOverlap
= 3;
38 const unsigned int Popup::MarginWidth
= 6;
40 Popup::Popup(QWidget
*parent
) :
41 QWidget(parent
, Qt::Popup
| Qt::FramelessWindowHint
),
47 const QPoint
& Popup::point() const
52 Popup::Position
Popup::position() const
57 void Popup::set_position(const QPoint point
, Position pos
)
59 point_
= point
, pos_
= pos
;
62 MarginWidth
+ ((pos
== Right
) ? ArrowLength
: 0),
63 MarginWidth
+ ((pos
== Bottom
) ? ArrowLength
: 0),
64 MarginWidth
+ ((pos
== Left
) ? ArrowLength
: 0),
65 MarginWidth
+ ((pos
== Top
) ? ArrowLength
: 0));
68 bool Popup::eventFilter(QObject
*obj
, QEvent
*event
)
74 if (event
->type() == QEvent::KeyPress
) {
75 keyEvent
= static_cast<QKeyEvent
*>(event
);
76 if (keyEvent
->key() == Qt::Key_Enter
||
77 keyEvent
->key() == Qt::Key_Return
) {
92 // We want to close the popup when the Enter key was
93 // pressed and the first editable widget had focus.
94 if ((le
= this->findChild
<QLineEdit
*>())) {
96 // For combo boxes we need to hook into the parent of
97 // the line edit (i.e. the QComboBox). For edit boxes
98 // we hook into the widget directly.
99 if (le
->parent()->metaObject()->className() ==
100 this->metaObject()->className())
101 le
->installEventFilter(this);
103 le
->parent()->installEventFilter(this);
110 bool Popup::space_for_arrow() const
112 // Check if there is room for the arrow
115 if (point_
.x() > x())
120 if (point_
.y() > y())
125 if (point_
.x() < (x() + width()))
130 if (point_
.y() < (y() + height()))
138 QPolygon
Popup::arrow_polygon() const
142 const QPoint p
= mapFromGlobal(point_
);
143 const int l
= ArrowLength
+ ArrowOverlap
;
147 poly
<< QPoint(p
.x() + l
, p
.y() - l
);
151 poly
<< QPoint(p
.x() - l
, p
.y() + l
);
156 poly
<< QPoint(p
.x() - l
, p
.y() - l
);
165 poly
<< QPoint(p
.x() + l
, p
.y() + l
);
169 poly
<< QPoint(p
.x() - l
, p
.y() + l
);
173 poly
<< QPoint(p
.x() + l
, p
.y() - l
);
180 QRegion
Popup::arrow_region() const
182 return QRegion(arrow_polygon());
185 QRect
Popup::bubble_rect() const
188 QPoint((pos_
== Right
) ? ArrowLength
: 0,
189 (pos_
== Bottom
) ? ArrowLength
: 0),
190 QSize(width() - ((pos_
== Left
|| pos_
== Right
) ?
192 height() - ((pos_
== Top
|| pos_
== Bottom
) ?
196 QRegion
Popup::bubble_region() const
198 const QRect
rect(bubble_rect());
200 const unsigned int r
= MarginWidth
;
201 const unsigned int d
= 2 * r
;
202 return QRegion(rect
.adjusted(r
, 0, -r
, 0)).united(
203 QRegion(rect
.adjusted(0, r
, 0, -r
))).united(
204 QRegion(rect
.left(), rect
.top(), d
, d
,
205 QRegion::Ellipse
)).united(
206 QRegion(rect
.right() - d
, rect
.top(), d
, d
,
207 QRegion::Ellipse
)).united(
208 QRegion(rect
.left(), rect
.bottom() - d
, d
, d
,
209 QRegion::Ellipse
)).united(
210 QRegion(rect
.right() - d
, rect
.bottom() - d
, d
, d
,
214 QRegion
Popup::popup_region() const
216 if (space_for_arrow())
217 return arrow_region().united(bubble_region());
219 return bubble_region();
222 void Popup::reposition_widget()
226 const QRect screen_rect
= QApplication::desktop()->availableGeometry(
227 QApplication::desktop()->screenNumber(point_
));
229 if (pos_
== Right
|| pos_
== Left
)
230 o
.ry() = -height() / 2;
232 o
.rx() = -width() / 2;
236 else if (pos_
== Top
)
240 move(max(min(o
.x(), screen_rect
.right() - width()),
242 max(min(o
.y(), screen_rect
.bottom() - height()),
246 void Popup::closeEvent(QCloseEvent
*)
251 void Popup::paintEvent(QPaintEvent
*)
253 QPainter
painter(this);
254 painter
.setRenderHint(QPainter::Antialiasing
);
256 const QColor
outline_color(QApplication::palette().color(
260 const QRegion b
= bubble_region();
261 const QRegion bubble_outline
= QRegion(rect()).subtracted(
262 b
.translated(1, 0).intersected(b
.translated(0, 1).intersected(
263 b
.translated(-1, 0).intersected(b
.translated(0, -1)))));
265 painter
.setPen(Qt::NoPen
);
266 painter
.setBrush(QApplication::palette().brush(QPalette::Window
));
267 painter
.drawRect(rect());
270 if (!space_for_arrow())
273 const QPoint ArrowOffsets
[] = {
274 QPoint(1, 0), QPoint(0, -1), QPoint(-1, 0), QPoint(0, 1)};
276 const QRegion
a(arrow_region());
277 const QRegion arrow_outline
= a
.subtracted(
278 a
.translated(ArrowOffsets
[pos_
]));
280 painter
.setClipRegion(bubble_outline
.subtracted(a
).united(
282 painter
.setBrush(outline_color
);
283 painter
.drawRect(rect());
286 void Popup::resizeEvent(QResizeEvent
*)
289 setMask(popup_region());
292 void Popup::mouseReleaseEvent(QMouseEvent
*event
)
296 // We need our own out-of-bounds click handler because QWidget counts
297 // the drop-shadow region as inside the widget
298 if (!bubble_rect().contains(event
->pos()))
302 void Popup::showEvent(QShowEvent
*)
307 } // namespace widgets