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
qquickfocusframe.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 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
6
7#include <private/qquickitem_p.h>
8
9#include <QtCore/qmetaobject.h>
10
11#include <QtGui/qguiapplication.h>
12
13#include <QtQml/qqmlengine.h>
14#include <QtQml/qqmlcontext.h>
15#include <QtQml/qqmlcomponent.h>
16
17#include <QtQuick/qquickitem.h>
18
19#include "items/qquickstyleitem.h"
20
22
23Q_LOGGING_CATEGORY(lcFocusFrame, "qt.quick.controls.focusframe")
24
25QQuickFocusFrameDescription QQuickFocusFrameDescription::Invalid = { nullptr, QQuickStyleMargins(), 0 };
26QScopedPointer<QQuickItem> QQuickFocusFrame::m_focusFrame;
27
28QQuickFocusFrame::QQuickFocusFrame()
29{
30 connect(qGuiApp, &QGuiApplication::focusObjectChanged, this, [this](QObject *focusObject){
31 if (auto item = qobject_cast<QQuickItem *>(focusObject))
32 moveToItem(item);
33 else
34 moveToItem(nullptr);
35 });
36}
37
38void QQuickFocusFrame::moveToItem(QQuickItem *item)
39{
40 if (!m_focusFrame) {
41 const auto context = QQmlEngine::contextForObject(item);
42 // In certain cases like QQuickWebEngineView, the item
43 // gets focus even though it has no QQmlEngine associated with its context.
44 // We need the engine for creating the focus frame component.
45 if (!context || !context->engine())
46 return;
47 m_focusFrame.reset(createFocusFrame(context));
48 if (!m_focusFrame) {
49 qWarning() << "Failed to create FocusFrame";
50 return;
51 }
52 QQuickItemPrivate::get(m_focusFrame.get())->setTransparentForPositioner(true);
53 }
54
55 const QQuickFocusFrameDescription &config = getDescriptionForItem(item);
56 QMetaObject::invokeMethod(m_focusFrame.data(), "moveToItem",
57 Q_ARG(QVariant, QVariant::fromValue(config.target)),
58 Q_ARG(QVariant, QVariant::fromValue(config.margins)),
59 Q_ARG(QVariant, QVariant::fromValue(config.radius)));
60}
61
62QQuickFocusFrameDescription QQuickFocusFrame::getDescriptionForItem(QQuickItem *focusItem) const
63{
64 if (!focusItem)
65 return QQuickFocusFrameDescription::Invalid;
66
67 qCDebug(lcFocusFrame) << "new focusobject:" << focusItem;
68 const auto parentItem = focusItem->parentItem();
69 if (!parentItem)
70 return QQuickFocusFrameDescription::Invalid;
71
72 // The item that gets active focus can be a child of the control (e.g
73 // editable ComboBox). In that case, resolve the actual control first.
74 const auto proxy = focusItem->property("__focusFrameControl").value<QQuickItem *>();
75 const auto control = proxy ? proxy : focusItem;
76 auto target = control->property("__focusFrameTarget").value<QQuickItem *>();
77 qCDebug(lcFocusFrame) << "target:" << target;
78 qCDebug(lcFocusFrame) << "control:" << control;
79
80 if (!target) {
81 // __focusFrameTarget points to the item in the control that should
82 // get the focus frame. This is usually the control itself, but can
83 // sometimes be a child (CheckBox). We anyway require
84 // this property to be set if we are to show the focus frame around
85 // the control in the first place. So for controls that don't want
86 // a frame (ProgressBar), we simply skip setting it.
87 // Also, we should never show a focus frame around custom controls.
88 // None of the built-in styles do that, so to be consistent, we
89 // shouldn't either. Besides, drawing a focus frame around an unknown
90 // item without any way to turn it off can easily be unwanted. A better
91 // way for custom controls to get a native focus frame is for us to offer
92 // a FocusFrame control (QTBUG-86818).
93 return QQuickFocusFrameDescription::Invalid;
94 }
95
96 // If the control gives us a QQuickStyleItem, we use that to configure the focus frame.
97 // By default we assume that the background delegate is a QQuickStyleItem, but the
98 // control can override this by setting __focusFrameStyleItem.
99 const auto styleItemProperty = control->property("__focusFrameStyleItem");
100 auto item = styleItemProperty.value<QQuickItem *>();
101 if (!item) {
102 const auto styleItemProperty = control->property("background");
103 item = styleItemProperty.value<QQuickItem *>();
104 }
105 qCDebug(lcFocusFrame) << "styleItem:" << item;
106 if (!item)
107 return QQuickFocusFrameDescription::Invalid;
108 if (QQuickStyleItem *styleItem = qobject_cast<QQuickStyleItem *>(item))
109 return { target, QQuickStyleMargins(styleItem->layoutMargins()), styleItem->focusFrameRadius() };
110
111 // Some controls don't have a QQuickStyleItem. But if the __focusFrameStyleItem
112 // has a "__focusFrameRadius" property set, we show a default focus frame using the specified radius instead.
113 const QVariant focusFrameRadiusVariant = item->property("__focusFrameRadius");
114 if (focusFrameRadiusVariant.isValid()) {
115 qCDebug(lcFocusFrame) << "'focusFrameRadius' property found, showing a default focus frame";
116 const QStyleOption opt;
117 const qreal radius = qMax(0.0, focusFrameRadiusVariant.toReal());
118 return { target, QQuickStyleMargins(), radius };
119 }
120
121 // The application has set a custom delegate on the control. In that
122 // case, it's the delegates responsibility to draw a focus frame.
123 qCDebug(lcFocusFrame) << "custom delegates in use, skip showing focus frame";
124 return QQuickFocusFrameDescription::Invalid;
125}
126
127QT_END_NAMESPACE
QQuickStyleMargins layoutMargins() const