1 | // Copyright (C) 2024 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 | |
4 | #include "qquickfluentwinui3focusframe_p.h" |
5 | |
6 | #include <QtCore/qmetaobject.h> |
7 | |
8 | #include <QtGui/qguiapplication.h> |
9 | |
10 | #include <QtQml/qqmlengine.h> |
11 | #include <QtQml/qqmlcontext.h> |
12 | #include <QtQml/qqmlcomponent.h> |
13 | |
14 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | QScopedPointer<QQuickItem> QQuickFluentWinUI3FocusFrame::m_focusFrame; |
18 | |
19 | QQuickFluentWinUI3FocusFrame::QQuickFluentWinUI3FocusFrame() |
20 | { |
21 | connect(qGuiApp, signal: &QGuiApplication::focusObjectChanged, context: this, slot: [this](QObject *focusObject){ |
22 | if (auto control = qobject_cast<QQuickControl *>(object: focusObject)) { |
23 | if (control->focusReason() == Qt::FocusReason::TabFocusReason |
24 | || control->focusReason() == Qt::FocusReason::BacktabFocusReason) { |
25 | moveToItem(item: control); |
26 | } |
27 | } else { |
28 | m_focusFrame.reset(); |
29 | } |
30 | }); |
31 | } |
32 | |
33 | QQuickItem *QQuickFluentWinUI3FocusFrame::createFocusFrame(QQmlContext *context) |
34 | { |
35 | QQmlComponent component( |
36 | context->engine(), |
37 | QUrl(QStringLiteral( |
38 | "qrc:/qt-project.org/imports/QtQuick/Controls/FluentWinUI3/FocusFrame.qml" ))); |
39 | auto frame = qobject_cast<QQuickItem *>(o: component.create()); |
40 | if (!frame) |
41 | return nullptr; |
42 | return frame; |
43 | } |
44 | |
45 | void QQuickFluentWinUI3FocusFrame::moveToItem(QQuickControl *item) |
46 | { |
47 | if (!m_focusFrame) { |
48 | const auto context = QQmlEngine::contextForObject(item); |
49 | // In certain cases like QQuickWebEngineView, the item |
50 | // gets focus even though it has no QQmlEngine associated with its context. |
51 | // We need the engine for creating the focus frame component. |
52 | if (!context || !context->engine()) |
53 | return; |
54 | m_focusFrame.reset(other: createFocusFrame(context)); |
55 | if (!m_focusFrame) { |
56 | qWarning() << "Failed to create FocusFrame" ; |
57 | return; |
58 | } |
59 | } |
60 | |
61 | const auto target = getFocusTarget(focusItem: item); |
62 | QMetaObject::invokeMethod(obj: m_focusFrame.data(), member: "moveToItem" , |
63 | Q_ARG(QVariant, QVariant::fromValue(target))); |
64 | } |
65 | |
66 | QQuickControl *QQuickFluentWinUI3FocusFrame::getFocusTarget(QQuickControl *focusItem) const |
67 | { |
68 | const auto parentItem = focusItem->parentItem(); |
69 | if (!parentItem) |
70 | return nullptr; |
71 | |
72 | // The control 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(name: "__focusFrameControl" ).value<QQuickControl *>(); |
75 | const auto control = proxy ? proxy : focusItem; |
76 | auto target = control->property(name: "__focusFrameTarget" ).value<QQuickControl *>(); |
77 | |
78 | return target; |
79 | } |
80 | |
81 | QT_END_NAMESPACE |
82 | |