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

KDE's Doxygen guidelines are available online.