Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfocusframe.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qfocusframe.h"
6#include "qstyle.h"
7#include "qbitmap.h"
9#include "qstyleoption.h"
10#include "qdebug.h"
11#include <private/qwidget_p.h>
12
14
16{
17 Q_DECLARE_PUBLIC(QFocusFrame)
19 QWidget *frameParent;
20 bool showFrameAboveWidget;
21public:
23 widget = nullptr;
24 frameParent = nullptr;
25 sendChildEvents = false;
26 showFrameAboveWidget = false;
27 }
28 void updateSize();
29 void update();
30};
31
33{
34 Q_Q(QFocusFrame);
35 q->setParent(frameParent);
37 if (q->parentWidget()->rect().intersects(q->geometry())) {
38 if (showFrameAboveWidget)
39 q->raise();
40 else
41 q->stackUnder(widget);
42 q->show();
43 } else {
44 q->hide();
45 }
46}
47
49{
50 Q_Q(QFocusFrame);
51 if (!widget)
52 return;
53
54 QStyleOption opt;
55 q->initStyleOption(&opt);
56 int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt, q),
57 hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &opt, q);
58 QPoint pos(widget->x(), widget->y());
59 if (q->parentWidget() != widget->parentWidget())
60 pos = widget->parentWidget()->mapTo(q->parentWidget(), pos);
61 QRect geom(pos.x()-hmargin, pos.y()-vmargin,
62 widget->width()+(hmargin*2), widget->height()+(vmargin*2));
63 if (q->geometry() == geom)
64 return;
65
66 q->setGeometry(geom);
67
68 opt.rect = q->rect();
70 if (q->style()->styleHint(QStyle::SH_FocusFrame_Mask, &opt, q, &mask))
71 q->setMask(mask.region);
72}
73
74/*!
75 Initialize \a option with the values from this QFocusFrame. This method is useful
76 for subclasses when they need a QStyleOption, but don't want to fill
77 in all the information themselves.
78
79 \sa QStyleOption::initFrom()
80*/
81void QFocusFrame::initStyleOption(QStyleOption *option) const
82{
83 if (!option)
84 return;
85
86 option->initFrom(this);
87}
88
89/*!
90 \class QFocusFrame
91 \brief The QFocusFrame widget provides a focus frame which can be
92 outside of a widget's normal paintable area.
93
94 \ingroup basicwidgets
95 \inmodule QtWidgets
96
97 Normally an application will not need to create its own
98 QFocusFrame as QStyle will handle this detail for
99 you. A style writer can optionally use a QFocusFrame to have a
100 focus area outside of the widget's paintable geometry. In this way
101 space need not be reserved for the widget to have focus but only
102 set on a QWidget with QFocusFrame::setWidget. It is, however,
103 legal to create your own QFocusFrame on a custom widget and set
104 its geometry manually via QWidget::setGeometry however you will
105 not get auto-placement when the focused widget changes size or
106 placement.
107*/
108
109/*!
110 Constructs a QFocusFrame.
111
112 The focus frame will not monitor \a parent for updates but rather
113 can be placed manually or by using QFocusFrame::setWidget. A
114 QFocusFrame sets Qt::WA_NoChildEventsForParent attribute; as a
115 result the parent will not receive a QEvent::ChildAdded event,
116 this will make it possible to manually set the geometry of the
117 QFocusFrame inside of a QSplitter or other child event monitoring
118 widget.
119
120 \sa QFocusFrame::setWidget()
121*/
122
123QFocusFrame::QFocusFrame(QWidget *parent)
124 : QWidget(*new QFocusFramePrivate, parent, { })
125{
126 setAttribute(Qt::WA_TransparentForMouseEvents);
127 setFocusPolicy(Qt::NoFocus);
128 setAttribute(Qt::WA_NoChildEventsForParent, true);
129 setAttribute(Qt::WA_AcceptDrops, style()->styleHint(QStyle::SH_FocusFrame_AboveWidget, nullptr, this));
130}
131
132/*!
133 Destructor.
134*/
135
136QFocusFrame::~QFocusFrame()
137{
138}
139
140/*!
141 QFocusFrame will track changes to \a widget and resize itself automatically.
142 If the monitored widget's parent changes, QFocusFrame will follow the widget
143 and place itself around the widget automatically. If the monitored widget is deleted,
144 QFocusFrame will set it to zero.
145
146 \sa QFocusFrame::widget()
147*/
148
149void
150QFocusFrame::setWidget(QWidget *widget)
151{
152 Q_D(QFocusFrame);
153
154 if (style()->styleHint(QStyle::SH_FocusFrame_AboveWidget, nullptr, this))
155 d->showFrameAboveWidget = true;
156 else
157 d->showFrameAboveWidget = false;
158
159 if (widget == d->widget)
160 return;
161 if (d->widget) {
162 // Remove event filters from the widget hierarchy.
163 QWidget *p = d->widget;
164 do {
165 p->removeEventFilter(this);
166 if (!d->showFrameAboveWidget || p == d->frameParent)
167 break;
168 p = p->parentWidget();
169 }while (p);
170 }
171 if (widget && !widget->isWindow() && widget->parentWidget()->windowType() != Qt::SubWindow) {
172 d->widget = widget;
173 d->widget->installEventFilter(this);
174 QWidget *p = widget->parentWidget();
175 QWidget *prev = nullptr;
176 if (d->showFrameAboveWidget) {
177 // Find the right parent for the focus frame.
178 while (p) {
179 // Traverse the hirerarchy of the 'widget' for setting event filter.
180 // During this if come across toolbar or a top level, use that
181 // as the parent for the focus frame. If we find a scroll area
182 // use its viewport as the parent.
183 bool isScrollArea = false;
184 if (p->isWindow() || p->inherits("QToolBar") || (isScrollArea = p->inherits("QAbstractScrollArea"))) {
185 d->frameParent = p;
186 // The previous one in the hierarchy will be the viewport.
187 if (prev && isScrollArea)
188 d->frameParent = prev;
189 break;
190 } else {
191 p->installEventFilter(this);
192 prev = p;
193 p = p->parentWidget();
194 }
195 }
196 } else {
197 d->frameParent = p;
198 }
199 d->update();
200 } else {
201 d->widget = nullptr;
202 hide();
203 }
204}
205
206/*!
207 Returns the currently monitored widget for automatically resize and
208 update.
209
210 \sa QFocusFrame::setWidget()
211*/
212
213QWidget *
214QFocusFrame::widget() const
215{
216 Q_D(const QFocusFrame);
217 return d->widget;
218}
219
220
221/*! \reimp */
222void
223QFocusFrame::paintEvent(QPaintEvent *)
224{
225 Q_D(QFocusFrame);
226
227 if (!d->widget)
228 return;
229
230 QStylePainter p(this);
231 QStyleOption option;
232 initStyleOption(&option);
233 const int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &option, this);
234 const int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, this);
235 QWidgetPrivate *wd = qt_widget_private(d->widget);
236 QRect rect = wd->clipRect().adjusted(0, 0, hmargin*2, vmargin*2);
237 p.setClipRect(rect);
238 p.drawControl(QStyle::CE_FocusFrame, option);
239}
240
241
242/*! \reimp */
243bool
244QFocusFrame::eventFilter(QObject *o, QEvent *e)
245{
246 Q_D(QFocusFrame);
247 if (o == d->widget) {
248 switch(e->type()) {
249 case QEvent::Move:
250 case QEvent::Resize:
251 d->updateSize();
252 break;
253 case QEvent::Hide:
254 case QEvent::StyleChange:
255 hide();
256 break;
257 case QEvent::ParentChange:
258 if (d->showFrameAboveWidget) {
259 QWidget *w = d->widget;
260 setWidget(nullptr);
261 setWidget(w);
262 } else {
263 d->update();
264 }
265 break;
266 case QEvent::Show:
267 d->update();
268 show();
269 break;
270 case QEvent::PaletteChange:
271 setPalette(d->widget->palette());
272 break;
273 case QEvent::ZOrderChange:
274 if (style()->styleHint(QStyle::SH_FocusFrame_AboveWidget, nullptr, this))
275 raise();
276 else
277 stackUnder(d->widget);
278 break;
279 case QEvent::Destroy:
280 setWidget(nullptr);
281 break;
282 default:
283 break;
284 }
285 } else if (d->showFrameAboveWidget) {
286 // Handle changes in the parent widgets we are monitoring.
287 switch(e->type()) {
288 case QEvent::Move:
289 case QEvent::Resize:
290 d->updateSize();
291 break;
292 case QEvent::ZOrderChange:
293 raise();
294 break;
295 default:
296 break;
297 }
298 }
299 return false;
300}
301
302/*! \reimp */
303bool QFocusFrame::event(QEvent *e)
304{
305 Q_D(QFocusFrame);
306
307 switch (e->type()) {
308 case QEvent::Move:
309 case QEvent::Resize:
310 if (d->widget) {
311 // When we're tracking a widget, we don't allow anyone to move the focus frame around.
312 // We do our best with event filters to make it stay on top of the widget, so trying to
313 // move the frame somewhere else will be flaky at best. This can e.g happen for general
314 // purpose code, like QAbstractScrollView, that bulk-moves all children a certain distance.
315 // So we need to call updateSize() when that happens to ensure that the focus frame stays
316 // on top of the widget.
317 d->updateSize();
318 }
319 break;
320 default:
321 return QWidget::event(e);
322 }
323 return true;
324}
325
326QT_END_NAMESPACE
327
328#include "moc_qfocusframe.cpp"
The QStyleHintReturnMask class provides style hints that return a QRegion.