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