KWidgetsAddons

ktooltipwidget.cpp
1 /*
2  This file is part of the KDE project
3  SPDX-FileCopyrightText: 2008 Fredrik Höglund <[email protected]>
4  SPDX-FileCopyrightText: 2008 Konstantin Heil <[email protected]>
5  SPDX-FileCopyrightText: 2009 Peter Penz <[email protected]>
6  SPDX-FileCopyrightText: 2017 Elvis Angelaccio <[email protected]>
7 
8  SPDX-License-Identifier: LGPL-2.1-or-later
9 */
10 
11 #include "ktooltipwidget.h"
12 
13 #include <QPaintEvent>
14 #include <QScreen>
15 #include <QStylePainter>
16 #include <QStyleOptionFrame>
17 #include <QTimer>
18 #include <QVBoxLayout>
19 #include <QWindow>
20 
21 class Q_DECL_HIDDEN KToolTipWidget::KToolTipWidgetPrivate
22 {
23 public:
24  KToolTipWidgetPrivate(KToolTipWidget *parent)
25  : q(parent)
26  {}
27 
28  void init();
29  void addWidget(QWidget *widget);
30  void removeWidget();
31  void show(const QPoint &pos, QWindow *transientParent);
32  void storeParent();
33  void restoreParent();
34  QPoint centerBelow(const QRect &rect, QScreen *screen) const;
35 
36  KToolTipWidget *const q;
37  QTimer hideTimer;
38  QVBoxLayout *layout = nullptr;
39  QWidget *content = nullptr;
40  QWidget *contentParent = nullptr;
41 };
42 
43 void KToolTipWidget::KToolTipWidgetPrivate::init()
44 {
45  layout = new QVBoxLayout(q);
46 
47  hideTimer.setSingleShot(true);
48  hideTimer.setInterval(500);
49 
50  connect(&hideTimer, &QTimer::timeout, q, &QWidget::hide);
51 
52  q->setAttribute(Qt::WA_TranslucentBackground);
54 }
55 
56 void KToolTipWidget::KToolTipWidgetPrivate::addWidget(QWidget *widget)
57 {
58  removeWidget();
59  content = widget;
60  storeParent();
61  layout->addWidget(content);
62  connect(content, &QWidget::destroyed, q, &QWidget::hide);
63 }
64 
65 void KToolTipWidget::KToolTipWidgetPrivate::removeWidget()
66 {
67  layout->removeWidget(content);
68  restoreParent();
69 }
70 
71 void KToolTipWidget::KToolTipWidgetPrivate::show(const QPoint &pos, QWindow *transientParent)
72 {
73  if (pos.isNull()) {
74  return;
75  }
76 
77  q->move(pos);
78  q->createWinId();
79  q->windowHandle()->setProperty("ENABLE_BLUR_BEHIND_HINT", true);
80  q->windowHandle()->setTransientParent(transientParent);
81  q->show();
82 }
83 
84 void KToolTipWidget::KToolTipWidgetPrivate::storeParent()
85 {
86  if (!content) {
87  return;
88  }
89 
90  contentParent = qobject_cast<QWidget*>(content->parent());
91 }
92 
93 void KToolTipWidget::KToolTipWidgetPrivate::restoreParent()
94 {
95  if (!content || !contentParent) {
96  return;
97  }
98 
99  content->setParent(contentParent);
100 }
101 
102 QPoint KToolTipWidget::KToolTipWidgetPrivate::centerBelow(const QRect &rect, QScreen *screen) const
103 {
104  // It must be assured that:
105  // - the content is fully visible
106  // - the content is not drawn inside rect
107 
108  const QSize size = q->sizeHint();
109  const int margin = q->style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth);
110  const QRect screenGeometry = screen->geometry();
111 
112  const bool hasRoomToLeft = (rect.left() - size.width() - margin >= screenGeometry.left());
113  const bool hasRoomToRight = (rect.right() + size.width() + margin <= screenGeometry.right());
114  const bool hasRoomAbove = (rect.top() - size.height() - margin >= screenGeometry.top());
115  const bool hasRoomBelow = (rect.bottom() + size.height() + margin <= screenGeometry.bottom());
116  if (!hasRoomAbove && !hasRoomBelow && !hasRoomToLeft && !hasRoomToRight) {
117  return QPoint();
118  }
119 
120  int x = 0;
121  int y = 0;
122  if (hasRoomBelow || hasRoomAbove) {
123  x = qMax(screenGeometry.left(), rect.center().x() - size.width() / 2);
124  if (x + size.width() >= screenGeometry.right()) {
125  x = screenGeometry.right() - size.width() + 1;
126  }
127  Q_ASSERT(x >= 0);
128  if (hasRoomBelow) {
129  y = rect.bottom() + margin;
130  } else {
131  y = rect.top() - size.height() - margin + 1;
132  }
133  } else {
134  Q_ASSERT(hasRoomToLeft || hasRoomToRight);
135  if (hasRoomToRight) {
136  x = rect.right() + margin;
137  } else {
138  x = rect.left() - size.width() - margin + 1;
139  }
140  // Put the tooltip at the bottom of the screen. The x-coordinate has already
141  // been adjusted, so that no overlapping with rect occurs.
142  y = screenGeometry.bottom() - size.height() + 1;
143  }
144 
145  return QPoint(x, y);
146 }
147 
148 KToolTipWidget::KToolTipWidget(QWidget *parent)
149  : QWidget(parent), d(new KToolTipWidgetPrivate(this))
150 {
151  d->init();
152 }
153 
154 KToolTipWidget::~KToolTipWidget()
155 {
156  d->restoreParent();
157 }
158 
159 void KToolTipWidget::showAt(const QPoint &pos, QWidget *content, QWindow *transientParent)
160 {
161  d->addWidget(content);
162  d->show(pos, transientParent);
163 }
164 
165 void KToolTipWidget::showBelow(const QRect &rect, QWidget *content, QWindow *transientParent)
166 {
167  d->addWidget(content);
168 
169  const auto contentMargins = layout()->contentsMargins();
170  const QSize screenSize = transientParent->screen()->geometry().size();
171 
172 #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
173  content->setMaximumSize(screenSize.shrunkBy(contentMargins));
174 #else
175  const QSize marginSize {
176  contentMargins.left() + contentMargins.right(),
177  contentMargins.top() + contentMargins.bottom(),
178  };
179  content->setMaximumSize(screenSize - marginSize);
180 #endif
181 
182  d->show(d->centerBelow(rect, transientParent->screen()), transientParent);
183 }
184 
185 int KToolTipWidget::hideDelay() const
186 {
187  return d->hideTimer.interval();
188 }
189 
191 {
192  if (!isVisible()) {
193  return;
194  }
195 
196  if (hideDelay() > 0) {
197  d->hideTimer.start();
198  } else {
199  hide();
200  }
201 }
202 
204 {
205  d->hideTimer.setInterval(delay);
206 }
207 
208 void KToolTipWidget::enterEvent(QEvent *)
209 {
210  // Ignore hide delay and leave tooltip visible.
211  if (hideDelay() > 0) {
212  d->hideTimer.stop();
213  } else {
214  hide();
215  }
216 }
217 
218 void KToolTipWidget::hideEvent(QHideEvent *)
219 {
220  d->removeWidget();
221  // Give time to the content widget to get his own hide event.
223 }
224 
225 void KToolTipWidget::leaveEvent(QEvent *)
226 {
227  // Don't bother starting the hide timer, we are done.
228  hide();
229 }
230 
231 void KToolTipWidget::paintEvent(QPaintEvent *event)
232 {
233  QStylePainter painter(this);
234  painter.setClipRegion(event->region());
235  QStyleOptionFrame option;
236  option.init(this);
237  painter.drawPrimitive(QStyle::PE_PanelTipLabel, option);
238  painter.end();
239 
240  QWidget::paintEvent(event);
241 }
242 
int width() const const
bool end()
QSize shrunkBy(QMargins margins) const const
void showAt(const QPoint &pos, QWidget *content, QWindow *transientParent)
Show a tooltip containing content.
int right() const const
const QRegion & region() const const
bool isVisible() const const
void setClipRegion(const QRegion &region, Qt::ClipOperation operation)
void setParent(QWidget *parent)
void drawPrimitive(QStyle::PrimitiveElement pe, const QStyleOption &option)
int x() const const
void timeout()
virtual void paintEvent(QPaintEvent *event)
int top() const const
void hideLater()
Hide the tooltip after a delay of hideDelay() ms (to allow interaction with the tooltip&#39;s widget)...
int left() const const
QPoint center() const const
A tooltip that contains a QWidget.
void showBelow(const QRect &rect, QWidget *content, QWindow *transientParent)
Show a tooltip containing content centered below rect.
void hide()
PE_PanelTipLabel
int hideDelay() const
QCA_EXPORT void init()
void setMaximumSize(const QSize &)
WA_TranslucentBackground
PM_ToolTipLabelFrameWidth
bool isNull() const const
int height() const const
int bottom() const const
void init(const QWidget *widget)
virtual bool event(QEvent *event) override
QScreen * screen() const const
void hidden()
The tooltip has been hidden and the tooltip&#39;s widget is no longer visible.
void destroyed(QObject *obj)
void setHideDelay(int delay)
Set after how many ms hideLater() will hide the tooltip.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Sep 17 2020 22:44:10 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.