PulseView  0.3.0
A Qt-based sigrok GUI
popup.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the PulseView project.
3  *
4  * Copyright (C) 2013 Joel Holdsworth <joel@airwebreathe.org.uk>
5  *
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.
10  *
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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <algorithm>
22 
23 #include <assert.h>
24 
25 #include <QtGui>
26 #include <QApplication>
27 #include <QDesktopWidget>
28 #include <QLineEdit>
29 
30 #include "popup.hpp"
31 
32 using std::max;
33 using std::min;
34 
35 namespace pv {
36 namespace widgets {
37 
38 const unsigned int Popup::ArrowLength = 10;
39 const unsigned int Popup::ArrowOverlap = 3;
40 const unsigned int Popup::MarginWidth = 6;
41 
42 Popup::Popup(QWidget *parent) :
43  QWidget(parent, Qt::Popup | Qt::FramelessWindowHint),
44  point_(),
45  pos_(Left)
46 {
47 }
48 
49 const QPoint& Popup::point() const
50 {
51  return point_;
52 }
53 
55 {
56  return pos_;
57 }
58 
59 void Popup::set_position(const QPoint point, Position pos)
60 {
61  point_ = point, pos_ = pos;
62 
63  setContentsMargins(
64  MarginWidth + ((pos == Right) ? ArrowLength : 0),
65  MarginWidth + ((pos == Bottom) ? ArrowLength : 0),
66  MarginWidth + ((pos == Left) ? ArrowLength : 0),
67  MarginWidth + ((pos == Top) ? ArrowLength : 0));
68 }
69 
70 bool Popup::eventFilter(QObject *obj, QEvent *evt)
71 {
72  QKeyEvent *keyEvent;
73 
74  (void)obj;
75 
76  if (evt->type() == QEvent::KeyPress) {
77  keyEvent = static_cast<QKeyEvent*>(evt);
78  if (keyEvent->key() == Qt::Key_Enter ||
79  keyEvent->key() == Qt::Key_Return) {
80  close();
81  return true;
82  }
83  }
84 
85  return false;
86 }
87 
89 {
90  QLineEdit* le;
91 
92  QWidget::show();
93 
94  // We want to close the popup when the Enter key was
95  // pressed and the first editable widget had focus.
96  if ((le = this->findChild<QLineEdit*>())) {
97 
98  // For combo boxes we need to hook into the parent of
99  // the line edit (i.e. the QComboBox). For edit boxes
100  // we hook into the widget directly.
101  if (le->parent()->metaObject()->className() ==
102  this->metaObject()->className())
103  le->installEventFilter(this);
104  else
105  le->parent()->installEventFilter(this);
106 
107  le->selectAll();
108  le->setFocus();
109  }
110 }
111 
113 {
114  // Check if there is room for the arrow
115  switch (pos_) {
116  case Right:
117  if (point_.x() > x())
118  return false;
119  return true;
120 
121  case Bottom:
122  if (point_.y() > y())
123  return false;
124  return true;
125 
126  case Left:
127  if (point_.x() < (x() + width()))
128  return false;
129  return true;
130 
131  case Top:
132  if (point_.y() < (y() + height()))
133  return false;
134  return true;
135  }
136 
137  return true;
138 }
139 
140 QPolygon Popup::arrow_polygon() const
141 {
142  QPolygon poly;
143 
144  const QPoint p = mapFromGlobal(point_);
145  const int l = ArrowLength + ArrowOverlap;
146 
147  switch (pos_) {
148  case Right:
149  poly << QPoint(p.x() + l, p.y() - l);
150  break;
151 
152  case Bottom:
153  poly << QPoint(p.x() - l, p.y() + l);
154  break;
155 
156  case Left:
157  case Top:
158  poly << QPoint(p.x() - l, p.y() - l);
159  break;
160  }
161 
162  poly << p;
163 
164  switch (pos_) {
165  case Right:
166  case Bottom:
167  poly << QPoint(p.x() + l, p.y() + l);
168  break;
169 
170  case Left:
171  poly << QPoint(p.x() - l, p.y() + l);
172  break;
173 
174  case Top:
175  poly << QPoint(p.x() + l, p.y() - l);
176  break;
177  }
178 
179  return poly;
180 }
181 
182 QRegion Popup::arrow_region() const
183 {
184  return QRegion(arrow_polygon());
185 }
186 
187 QRect Popup::bubble_rect() const
188 {
189  return QRect(
190  QPoint((pos_ == Right) ? ArrowLength : 0,
191  (pos_ == Bottom) ? ArrowLength : 0),
192  QSize(width() - ((pos_ == Left || pos_ == Right) ?
193  ArrowLength : 0),
194  height() - ((pos_ == Top || pos_ == Bottom) ?
195  ArrowLength : 0)));
196 }
197 
198 QRegion Popup::bubble_region() const
199 {
200  const QRect rect(bubble_rect());
201 
202  const unsigned int r = MarginWidth;
203  const unsigned int d = 2 * r;
204  return QRegion(rect.adjusted(r, 0, -r, 0)).united(
205  QRegion(rect.adjusted(0, r, 0, -r))).united(
206  QRegion(rect.left(), rect.top(), d, d,
207  QRegion::Ellipse)).united(
208  QRegion(rect.right() - d, rect.top(), d, d,
209  QRegion::Ellipse)).united(
210  QRegion(rect.left(), rect.bottom() - d, d, d,
211  QRegion::Ellipse)).united(
212  QRegion(rect.right() - d, rect.bottom() - d, d, d,
213  QRegion::Ellipse));
214 }
215 
216 QRegion Popup::popup_region() const
217 {
218  if (space_for_arrow())
219  return arrow_region().united(bubble_region());
220  else
221  return bubble_region();
222 }
223 
225 {
226  QPoint o;
227 
228  const QRect screen_rect = QApplication::desktop()->availableGeometry(
229  QApplication::desktop()->screenNumber(point_));
230 
231  if (pos_ == Right || pos_ == Left)
232  o.ry() = -height() / 2;
233  else
234  o.rx() = -width() / 2;
235 
236  if (pos_ == Left)
237  o.rx() = -width();
238  else if (pos_ == Top)
239  o.ry() = -height();
240 
241  o += point_;
242  move(max(min(o.x(), screen_rect.right() - width()),
243  screen_rect.left()),
244  max(min(o.y(), screen_rect.bottom() - height()),
245  screen_rect.top()));
246 }
247 
248 void Popup::closeEvent(QCloseEvent*)
249 {
250  closed();
251 }
252 
253 void Popup::paintEvent(QPaintEvent*)
254 {
255  QPainter painter(this);
256  painter.setRenderHint(QPainter::Antialiasing);
257 
258  const QColor outline_color(QApplication::palette().color(
259  QPalette::Dark));
260 
261  // Draw the bubble
262  const QRegion b = bubble_region();
263  const QRegion bubble_outline = QRegion(rect()).subtracted(
264  b.translated(1, 0).intersected(b.translated(0, 1).intersected(
265  b.translated(-1, 0).intersected(b.translated(0, -1)))));
266 
267  painter.setPen(Qt::NoPen);
268  painter.setBrush(QApplication::palette().brush(QPalette::Window));
269  painter.drawRect(rect());
270 
271  // Draw the arrow
272  if (!space_for_arrow())
273  return;
274 
275  const QPoint ArrowOffsets[] = {
276  QPoint(1, 0), QPoint(0, -1), QPoint(-1, 0), QPoint(0, 1)};
277 
278  const QRegion a(arrow_region());
279  const QRegion arrow_outline = a.subtracted(
280  a.translated(ArrowOffsets[pos_]));
281 
282  painter.setClipRegion(bubble_outline.subtracted(a).united(
283  arrow_outline));
284  painter.setBrush(outline_color);
285  painter.drawRect(rect());
286 }
287 
288 void Popup::resizeEvent(QResizeEvent*)
289 {
291  setMask(popup_region());
292 }
293 
294 void Popup::mouseReleaseEvent(QMouseEvent *e)
295 {
296  assert(e);
297 
298  // We need our own out-of-bounds click handler because QWidget counts
299  // the drop-shadow region as inside the widget
300  if (!bubble_rect().contains(e->pos()))
301  close();
302 }
303 
304 void Popup::showEvent(QShowEvent*)
305 {
307 }
308 
309 } // namespace widgets
310 } // namespace pv
bool space_for_arrow() const
Definition: popup.cpp:112
void closeEvent(QCloseEvent *)
Definition: popup.cpp:248
void reposition_widget()
Definition: popup.cpp:224
void showEvent(QShowEvent *e)
Definition: popup.cpp:304
void set_position(const QPoint point, Position pos)
Definition: popup.cpp:59
QRegion arrow_region() const
Definition: popup.cpp:182
bool eventFilter(QObject *obj, QEvent *evt)
Definition: popup.cpp:70
void resizeEvent(QResizeEvent *)
Definition: popup.cpp:288
void mouseReleaseEvent(QMouseEvent *e)
Definition: popup.cpp:294
QRegion bubble_region() const
Definition: popup.cpp:198
Position position() const
Definition: popup.cpp:54
void paintEvent(QPaintEvent *)
Definition: popup.cpp:253
QRegion popup_region() const
Definition: popup.cpp:216
const QPoint & point() const
Definition: popup.cpp:49
QPolygon arrow_polygon() const
Definition: popup.cpp:140
static const unsigned int ArrowOverlap
Definition: popup.hpp:44
static const unsigned int ArrowLength
Definition: popup.hpp:43
Position pos_
Definition: popup.hpp:91
Popup(QWidget *parent)
Definition: popup.cpp:42
QRect bubble_rect() const
Definition: popup.cpp:187
static const unsigned int MarginWidth
Definition: popup.hpp:45